From c211c02b95ab684c1582d516e0942bd0820aa942 Mon Sep 17 00:00:00 2001 From: ixfoduap Date: Wed, 24 Feb 2021 09:01:53 -0500 Subject: [PATCH 01/50] First version of new excitation operations --- pennylane/devices/default_qubit.py | 6 + pennylane/ops/qubit.py | 182 +++++++++++++++++++++++++++++ 2 files changed, 188 insertions(+) diff --git a/pennylane/devices/default_qubit.py b/pennylane/devices/default_qubit.py index 5443bfcd0ae..ec66d397b15 100644 --- a/pennylane/devices/default_qubit.py +++ b/pennylane/devices/default_qubit.py @@ -124,6 +124,12 @@ class DefaultQubit(QubitDevice): "CRZ", "CRot", "QFT", + "G1Y", + "G1Yplus", + "G1Yminus", + "G2Y", + "G2Yplus", + "G2Yminus" } observables = {"PauliX", "PauliY", "PauliZ", "Hadamard", "Hermitian", "Identity"} diff --git a/pennylane/ops/qubit.py b/pennylane/ops/qubit.py index 12f10c70721..5ce80bd0e89 100644 --- a/pennylane/ops/qubit.py +++ b/pennylane/ops/qubit.py @@ -1565,6 +1565,182 @@ def decomposition(theta, phi, lam, wires): ] return decomp_ops +# ============================================================================= +# Quantum chemistry +# ============================================================================= + + +class G1Yminus(Operation): + r""" + """ + num_params = 1 + num_wires = 2 + par_domain = "R" + grad_method = "A" + generator = [np.array([[1, 0, 0, 0], [0, 0, -1j, 0], [0, 1j, 0, 0], [0, 0, 0, 1]]), -1 / 2] + + @classmethod + def _matrix(cls, *params): + theta = params[0] + c = math.cos(theta / 2) + s = math.sin(theta / 2) + e = math.exp(-1j * theta / 2) + + return np.array([[e, 0, 0, 0], [0, c, -s, 0], [0, s, c, 0], [0, 0, 0, e]]) + + +class G1Yplus(Operation): + r""" + """ + num_params = 1 + num_wires = 2 + par_domain = "R" + grad_method = "A" + generator = [np.array([[-1, 0, 0, 0], [0, 0, -1j, 0], [0, 1j, 0, 0], [0, 0, 0, -1]]), -1 / 2] + + @classmethod + def _matrix(cls, *params): + theta = params[0] + c = math.cos(theta / 2) + s = math.sin(theta / 2) + e = math.exp(1j * theta / 2) + + return np.array([[e, 0, 0, 0], [0, c, -s, 0], [0, s, c, 0], [0, 0, 0, e]]) + + +class G1Y(Operation): + r""" + """ + num_params = 1 + num_wires = 2 + par_domain = "R" + grad_method = "A" + generator = [np.array([[0, 0, 0, 0], [0, 0, -1j, 0], [0, 1j, 0, 0], [0, 0, 0, 0]]), -1 / 2] + + @classmethod + def _matrix(cls, *params): + theta = params[0] + c = math.cos(theta / 2) + s = math.sin(theta / 2) + + return np.array([[1, 0, 0, 0], [0, c, -s, 0], [0, s, c, 0], [0, 0, 0, 1]]) + + @staticmethod + def decomposition(theta, wires): + decomp_ops = [G1Yplus(theta / 2, wires=wires), G1Yminus(theta / 2, wires=wires)] + return decomp_ops + + +class G2Yminus(Operation): + r""" + """ + num_params = 1 + num_wires = 4 + par_domain = "R" + grad_method = "A" + + A = np.eye(16, dtype=np.complex64) + A[3] = A[12] = np.zeros(16, dtype=np.complex64) + A[3, 12] = -1j + A[12, 3] = 1j + generator = [A, -1 / 2] + + @classmethod + def _matrix(cls, *params): + theta = params[0] + c = math.cos(theta / 2) + s = math.sin(theta / 2) + e = math.exp(-1j * theta / 2) + + B = e * np.eye(16, dtype=np.complex64) + B[3] = B[12] = np.zeros(16, dtype=np.complex64) + B[3, 3] = c + B[3, 12] = -s + B[12, 3] = s + B[12, 12] = c + + return B + + +class G2Yplus(Operation): + r""" + """ + num_params = 1 + num_wires = 4 + par_domain = "R" + grad_method = "A" + + A = np.eye(16, dtype=np.complex64) + A[3] = A[12] = np.zeros(16, dtype=np.complex64) + A[3, 12] = -1j + A[12, 3] = 1j + generator = [A, -1 / 2] + + @classmethod + def _matrix(cls, *params): + theta = params[0] + c = math.cos(theta / 2) + s = math.sin(theta / 2) + e = math.exp(1j * theta / 2) + + B = e * np.eye(16, dtype=np.complex64) + B[3] = B[12] = np.zeros(16, dtype=np.complex64) + B[3, 3] = c + B[3, 12] = -s + B[12, 3] = s + B[12, 12] = c + + return B + + +class G2Y(Operation): + r""" + """ + num_params = 1 + num_wires = 4 + par_domain = "R" + grad_method = "A" + + A = np.zeros((16, 16), dtype=np.complex64) + A[3, 12] = -1j + A[12, 3] = 1j + generator = [A, -1 / 2] + + @classmethod + def _matrix(cls, *params): + theta = params[0] + c = math.cos(theta / 2) + s = math.sin(theta / 2) + + # B = np.eye(16, dtype=np.complex64) + # B[3] = B[12] = np.zeros(16, dtype=np.complex64) # 3 = 0011, # 12 = 1100 + # B[3, 3] = c + # B[3, 12] = -s + # B[12, 3] = s + # B[12, 12] = c + + return np.array([[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, c, 0, 0, 0, 0, 0, 0, 0, 0, -s, 0, 0, 0], + [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], + [0, 0, 0, s, 0, 0, 0, 0, 0, 0, 0, 0, c, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]] + ) + + @staticmethod + def decomposition(theta, wires): + decomp_ops = [G2Yplus(theta / 2, wires=wires), G2Yminus(theta / 2, wires=wires)] + return decomp_ops # ============================================================================= # Arbitrary operations @@ -1977,6 +2153,12 @@ def diagonalizing_gates(self): "ControlledQubitUnitary", "DiagonalQubitUnitary", "QFT", + "G1Y", + "G1Yplus", + "G1Yminus", + "G2Y", + "G2Yplus", + "G2Yminus" } From c9aa6d407cba43ece6b6b0434a68b8af88b601e1 Mon Sep 17 00:00:00 2001 From: ixfoduap Date: Tue, 2 Mar 2021 15:51:59 -0500 Subject: [PATCH 02/50] Adds docstrings + interface operations --- pennylane/devices/autograd_ops.py | 50 ++++++++ pennylane/devices/jax_ops.py | 50 ++++++++ pennylane/devices/tf_ops.py | 53 +++++++++ pennylane/ops/qubit.py | 188 +++++++++++++----------------- 4 files changed, 231 insertions(+), 110 deletions(-) diff --git a/pennylane/devices/autograd_ops.py b/pennylane/devices/autograd_ops.py index d13361cd218..49b73334b0a 100644 --- a/pennylane/devices/autograd_ops.py +++ b/pennylane/devices/autograd_ops.py @@ -180,3 +180,53 @@ def MultiRZ(theta, n): array[complex]: diagonal part of the multi-qubit rotation matrix """ return np.exp(-1j * theta / 2 * pauli_eigs(n)) + + +def SingleExcitation(phi): + r""" + Single excitation rotation. + + Args: + phi (float): rotation angle + + Returns: + jnp.Tensor[complex]: Single excitation rotation matrix + + """ + c = np.cos(phi / 2) + s = np.sin(phi / 2) + return np.array([[1, 0, 0, 0], [0, c, -s, 0], [0, s, c, 0], [0, 0, 0, 1]]) + + +def SingleExcitationPlus(phi): + r""" + Single excitation rotation with positive phase-shift outside the rotation subspace. + + Args: + phi (float): rotation angle + + Returns: + jnp.Tensor[complex]: Single excitation rotation matrix with positive phase-shift + + """ + c = np.cos(phi / 2) + s = np.sin(phi / 2) + e = np.exp(1j * phi / 2) + return np.array([[e, 0, 0, 0], [0, c, -s, 0], [0, s, c, 0], [0, 0, 0, e]]) + + +def SingleExcitationMinus(phi): + r""" + Single excitation rotation with negative phase-shift outside the rotation subspace. + + Args: + phi (float): rotation angle + + Returns: + tf.Tensor[complex]: Single excitation rotation matrix with negative phase-shift + + """ + c = np.cos(phi / 2) + s = np.sin(phi / 2) + e = np.exp(-1j * phi / 2) + return np.array([[e, 0, 0, 0], [0, c, -s, 0], [0, s, c, 0], [0, 0, 0, e]]) diff --git a/pennylane/devices/jax_ops.py b/pennylane/devices/jax_ops.py index 0e7e6a29c6a..5dbd532b843 100644 --- a/pennylane/devices/jax_ops.py +++ b/pennylane/devices/jax_ops.py @@ -180,3 +180,53 @@ def MultiRZ(theta, n): array[complex]: diagonal part of the multi-qubit rotation matrix """ return jnp.exp(-1j * theta / 2 * pauli_eigs(n)) + + +def SingleExcitation(phi): + r""" + Single excitation rotation. + + Args: + phi (float): rotation angle + + Returns: + jnp.Tensor[complex]: Single excitation rotation matrix + + """ + c = jnp.cos(phi / 2) + s = jnp.sin(phi / 2) + return jnp.array([[1, 0, 0, 0], [0, c, -s, 0], [0, s, c, 0], [0, 0, 0, 1]]) + + +def SingleExcitationPlus(phi): + r""" + Single excitation rotation with positive phase-shift outside the rotation subspace. + + Args: + phi (float): rotation angle + + Returns: + jnp.Tensor[complex]: Single excitation rotation matrix with positive phase-shift + + """ + c = jnp.cos(phi / 2) + s = jnp.sin(phi / 2) + e = jnp.exp(1j * phi / 2) + return jnp.array([[e, 0, 0, 0], [0, c, -s, 0], [0, s, c, 0], [0, 0, 0, e]]) + + +def SingleExcitationMinus(phi): + r""" + Single excitation rotation with negative phase-shift outside the rotation subspace. + + Args: + phi (float): rotation angle + + Returns: + tf.Tensor[complex]: Single excitation rotation matrix with negative phase-shift + + """ + c = jnp.cos(phi / 2) + s = jnp.sin(phi / 2) + e = jnp.exp(-1j * phi / 2) + return jnp.array([[e, 0, 0, 0], [0, c, -s, 0], [0, s, c, 0], [0, 0, 0, e]]) \ No newline at end of file diff --git a/pennylane/devices/tf_ops.py b/pennylane/devices/tf_ops.py index f01563f5a7f..6b79ceb3d5d 100644 --- a/pennylane/devices/tf_ops.py +++ b/pennylane/devices/tf_ops.py @@ -191,3 +191,56 @@ def CRot(a, b, c): :math:`|0\rangle\langle 0|\otimes \mathbb{I}+|1\rangle\langle 1|\otimes R(a,b,c)` """ return tf.linalg.diag(CRZ(c)) @ (CRY(b) @ tf.linalg.diag(CRZ(a))) + + +def SingleExcitation(phi): + r""" + Single excitation rotation. + + Args: + phi (float): rotation angle + + Returns: + tf.Tensor[complex]: Single excitation rotation matrix + + """ + phi = tf.cast(phi, dtype=C_DTYPE) + c = tf.cos(phi / 2) + s = tf.sin(phi / 2) + return tf.convert_to_tensor([[1, 0, 0, 0], [0, c, -s, 0], [0, s, c, 0], [0, 0, 0, 1]]) + + +def SingleExcitationPlus(phi): + r""" + Single excitation rotation with positive phase-shift outside the rotation subspace. + + Args: + phi (float): rotation angle + + Returns: + tf.Tensor[complex]: Single excitation rotation matrix with positive phase-shift + + """ + phi = tf.cast(phi, dtype=C_DTYPE) + c = tf.cos(phi / 2) + s = tf.sin(phi / 2) + e = tf.exp(1j * phi / 2) + return tf.convert_to_tensor([[e, 0, 0, 0], [0, c, -s, 0], [0, s, c, 0], [0, 0, 0, e]]) + + +def SingleExcitationMinus(phi): + r""" + Single excitation rotation with negative phase-shift outside the rotation subspace. + + Args: + phi (float): rotation angle + + Returns: + tf.Tensor[complex]: Single excitation rotation matrix with negative phase-shift + + """ + phi = tf.cast(phi, dtype=C_DTYPE) + c = tf.cos(phi / 2) + s = tf.sin(phi / 2) + e = tf.exp(-1j * phi / 2) + return tf.convert_to_tensor([[e, 0, 0, 0], [0, c, -s, 0], [0, s, c, 0], [0, 0, 0, e]]) \ No newline at end of file diff --git a/pennylane/ops/qubit.py b/pennylane/ops/qubit.py index 5ce80bd0e89..b24a175ab5d 100644 --- a/pennylane/ops/qubit.py +++ b/pennylane/ops/qubit.py @@ -1569,48 +1569,46 @@ def decomposition(theta, phi, lam, wires): # Quantum chemistry # ============================================================================= - -class G1Yminus(Operation): +class SingleExcitation(Operation): r""" - """ - num_params = 1 - num_wires = 2 - par_domain = "R" - grad_method = "A" - generator = [np.array([[1, 0, 0, 0], [0, 0, -1j, 0], [0, 1j, 0, 0], [0, 0, 0, 1]]), -1 / 2] + Single excitation rotation. - @classmethod - def _matrix(cls, *params): - theta = params[0] - c = math.cos(theta / 2) - s = math.sin(theta / 2) - e = math.exp(-1j * theta / 2) + .. math:: U(\phi) = \begin{bmatrix} + 1 & 0 & 0 & 0 \\ + 0 & \cos(\phi/2) & -\sin(\phi/2) & 0 \\ + 0 & \sin(\phi/2) & \cos(\phi/2) & 0 \\ + 0 & 0 & 0 & 1 + \end{bmatrix}. - return np.array([[e, 0, 0, 0], [0, c, -s, 0], [0, s, c, 0], [0, 0, 0, e]]) + This operation performs a rotation in the two-dimensional subspace :math:`\{|01\rangle, + |10\rangle\}`. + **Details:** -class G1Yplus(Operation): - r""" - """ - num_params = 1 - num_wires = 2 - par_domain = "R" - grad_method = "A" - generator = [np.array([[-1, 0, 0, 0], [0, 0, -1j, 0], [0, 1j, 0, 0], [0, 0, 0, -1]]), -1 / 2] + * Number of wires: 2 + * Number of parameters: 1 + * Gradient recipe: Obtained from its decomposition in terms of the ``SingleExcitationPlus`` and + ``SingleExcitationMinus`` operations - @classmethod - def _matrix(cls, *params): - theta = params[0] - c = math.cos(theta / 2) - s = math.sin(theta / 2) - e = math.exp(1j * theta / 2) + Args: + phi (float): rotation angle :math:`\phi` + wires (Sequence[int] or int): the wires the operation acts on - return np.array([[e, 0, 0, 0], [0, c, -s, 0], [0, s, c, 0], [0, 0, 0, e]]) + **Example** + The following circuit performs the transformation :math:`|10>\rightarrow \cos( + \phi/2)|10\rangle -\sin(\phi/2)|01\rangle)`: -class G1Y(Operation): - r""" + .. code-block:: + + dev = qml.device('default.qubit', wires=2) + + @qml.qnode(dev) + def circuit(phi): + qml.PauliX(wires=0) + qml.SingleExcitation(phi, wires=[0, 1]) """ + num_params = 1 num_wires = 2 par_domain = "R" @@ -1627,23 +1625,40 @@ def _matrix(cls, *params): @staticmethod def decomposition(theta, wires): - decomp_ops = [G1Yplus(theta / 2, wires=wires), G1Yminus(theta / 2, wires=wires)] + decomp_ops = [SingleExcitationPlus(theta / 2, wires=wires), + SingleExcitationMinus(theta / 2, wires=wires)] return decomp_ops -class G2Yminus(Operation): +class SingleExcitationMinus(Operation): r""" + Single excitation rotation with negative phase-shift outside the rotation subspace. + + .. math:: U_-(\phi) = \begin{bmatrix} + e^{-i\phi/2} & 0 & 0 & 0 \\ + 0 & \cos(\phi/2) & -\sin(\phi/2) & 0 \\ + 0 & \sin(\phi/2) & \cos(\phi/2) & 0 \\ + 0 & 0 & 0 & e^{-i\phi/2} + \end{bmatrix}. + + **Details:** + + * Number of wires: 2 + * Number of parameters: 1 + * Gradient recipe: :math:`\frac{d}{d\phi}f(R_y(\phi)) = U_-\frac{1}{2}\left[f(U_+(\phi+\pi/2)) - + f(U_-(\phi-\pi/2))\right]` where :math:`f` is an expectation value depending on :math:`U_-( + \phi)`. + + Args: + phi (float): rotation angle :math:`\phi` + wires (Sequence[int] or int): the wires the operation acts on + """ num_params = 1 - num_wires = 4 + num_wires = 2 par_domain = "R" grad_method = "A" - - A = np.eye(16, dtype=np.complex64) - A[3] = A[12] = np.zeros(16, dtype=np.complex64) - A[3, 12] = -1j - A[12, 3] = 1j - generator = [A, -1 / 2] + generator = [np.array([[1, 0, 0, 0], [0, 0, -1j, 0], [0, 1j, 0, 0], [0, 0, 0, 1]]), -1 / 2] @classmethod def _matrix(cls, *params): @@ -1652,95 +1667,48 @@ def _matrix(cls, *params): s = math.sin(theta / 2) e = math.exp(-1j * theta / 2) - B = e * np.eye(16, dtype=np.complex64) - B[3] = B[12] = np.zeros(16, dtype=np.complex64) - B[3, 3] = c - B[3, 12] = -s - B[12, 3] = s - B[12, 12] = c - - return B + return np.array([[e, 0, 0, 0], [0, c, -s, 0], [0, s, c, 0], [0, 0, 0, e]]) -class G2Yplus(Operation): +class SingleExcitationPlus(Operation): r""" - """ - num_params = 1 - num_wires = 4 - par_domain = "R" - grad_method = "A" + Single excitation rotation with positive phase-shift outside the rotation subspace. - A = np.eye(16, dtype=np.complex64) - A[3] = A[12] = np.zeros(16, dtype=np.complex64) - A[3, 12] = -1j - A[12, 3] = 1j - generator = [A, -1 / 2] - - @classmethod - def _matrix(cls, *params): - theta = params[0] - c = math.cos(theta / 2) - s = math.sin(theta / 2) - e = math.exp(1j * theta / 2) + .. math:: U_+(\phi) = \begin{bmatrix} + e^{i\phi/2} & 0 & 0 & 0 \\ + 0 & \cos(\phi/2) & -\sin(\phi/2) & 0 \\ + 0 & \sin(\phi/2) & \cos(\phi/2) & 0 \\ + 0 & 0 & 0 & e^{i\phi/2} + \end{bmatrix}. - B = e * np.eye(16, dtype=np.complex64) - B[3] = B[12] = np.zeros(16, dtype=np.complex64) - B[3, 3] = c - B[3, 12] = -s - B[12, 3] = s - B[12, 12] = c + **Details:** - return B + * Number of wires: 2 + * Number of parameters: 1 + * Gradient recipe: :math:`\frac{d}{d\phi}f(R_y(\phi)) = U_+\frac{1}{2}\left[f(U_+(\phi+\pi/2)) - + f(U_+(\phi-\pi/2))\right]` where :math:`f` is an expectation value depending on + :math:`U_+(\phi)`. + Args: + phi (float): rotation angle :math:`\phi` + wires (Sequence[int] or int): the wires the operation acts on -class G2Y(Operation): - r""" """ num_params = 1 - num_wires = 4 + num_wires = 2 par_domain = "R" grad_method = "A" - - A = np.zeros((16, 16), dtype=np.complex64) - A[3, 12] = -1j - A[12, 3] = 1j - generator = [A, -1 / 2] + generator = [np.array([[-1, 0, 0, 0], [0, 0, -1j, 0], [0, 1j, 0, 0], [0, 0, 0, -1]]), -1 / 2] @classmethod def _matrix(cls, *params): theta = params[0] c = math.cos(theta / 2) s = math.sin(theta / 2) + e = math.exp(1j * theta / 2) - # B = np.eye(16, dtype=np.complex64) - # B[3] = B[12] = np.zeros(16, dtype=np.complex64) # 3 = 0011, # 12 = 1100 - # B[3, 3] = c - # B[3, 12] = -s - # B[12, 3] = s - # B[12, 12] = c - - return np.array([[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, c, 0, 0, 0, 0, 0, 0, 0, 0, -s, 0, 0, 0], - [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], - [0, 0, 0, s, 0, 0, 0, 0, 0, 0, 0, 0, c, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]] - ) + return np.array([[e, 0, 0, 0], [0, c, -s, 0], [0, s, c, 0], [0, 0, 0, e]]) - @staticmethod - def decomposition(theta, wires): - decomp_ops = [G2Yplus(theta / 2, wires=wires), G2Yminus(theta / 2, wires=wires)] - return decomp_ops # ============================================================================= # Arbitrary operations From 143cbe384db35d20780bb9be242830062aaa3fc3 Mon Sep 17 00:00:00 2001 From: ixfoduap Date: Wed, 3 Mar 2021 15:44:54 -0500 Subject: [PATCH 03/50] Adds support across devices and changelog snippet --- .github/CHANGELOG.md | 22 +++++++++++++++++++++ doc/introduction/operations.rst | 3 +++ pennylane/devices/default_mixed.py | 3 +++ pennylane/devices/default_qubit.py | 9 +++------ pennylane/devices/default_qubit_autograd.py | 3 +++ pennylane/devices/default_qubit_jax.py | 3 +++ pennylane/devices/default_qubit_tf.py | 3 +++ 7 files changed, 40 insertions(+), 6 deletions(-) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 9ed336155b4..63d77127e7d 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -2,6 +2,28 @@

New features since last release

+- Added the `SingleExcitation` two-qubit operation, which is particularly useful for quantum + chemistry applications. [(#1095)](https://github.com/PennyLaneAI/pennylane/pull/1095) + It can be used to perform an :math:`SO(2)` rotation in the subspace + spanned by the states :math:`|01\rangle, |10\rangle`. For example, the following circuit + performs the transformation :math:`|10>\rightarrow \cos(\phi/2)|10\rangle -\sin(\phi/2)|01\rangle)`: + + ```python + dev = qml.device('default.qubit', wires=2) + + @qml.qnode(dev) + def circuit(phi): + qml.PauliX(wires=0) + qml.SingleExcitation(phi, wires=[0, 1]) + ``` + + The `SingleExcitation` operation supports analytical gradients via its decomposition + in terms of the `SingleExcitationPlus` and `SingleExcitationMinus` operations, whose + gradients are given by the standard parameter-shift rule. These are also now supported. + Instead of acting as the identity on the subspace :math:`|00\rangle, |11\rangle`, the + `SingleExcitationPlus` and `SingleExcitationMinus` operations respectively apply a + positive/negative phase-shift on this subspace. + - Added the `QuantumPhaseEstimation` template for performing quantum phase estimation for an input unitary matrix. [(#1095)](https://github.com/PennyLaneAI/pennylane/pull/1095) diff --git a/doc/introduction/operations.rst b/doc/introduction/operations.rst index c6fc137513b..ab8e57d6649 100644 --- a/doc/introduction/operations.rst +++ b/doc/introduction/operations.rst @@ -84,6 +84,9 @@ Qubit gates ~pennylane.ControlledQubitUnitary ~pennylane.DiagonalQubitUnitary ~pennylane.QFT + ~pennylane.SingleExcitation + ~pennylane.SingleExcitationPlus + ~pennylane.SingleExcitationMinus :html:`` diff --git a/pennylane/devices/default_mixed.py b/pennylane/devices/default_mixed.py index 2ef48c90471..564d77890fc 100644 --- a/pennylane/devices/default_mixed.py +++ b/pennylane/devices/default_mixed.py @@ -95,6 +95,9 @@ class DefaultMixed(QubitDevice): "PhaseFlip", "QubitChannel", "QFT", + "SingleExcitation", + "SingleExcitationPlus", + "SingleExcitationMinus", } def __init__(self, wires, *, shots=1000, analytic=True, cache=0): diff --git a/pennylane/devices/default_qubit.py b/pennylane/devices/default_qubit.py index ec66d397b15..bef36762477 100644 --- a/pennylane/devices/default_qubit.py +++ b/pennylane/devices/default_qubit.py @@ -124,12 +124,9 @@ class DefaultQubit(QubitDevice): "CRZ", "CRot", "QFT", - "G1Y", - "G1Yplus", - "G1Yminus", - "G2Y", - "G2Yplus", - "G2Yminus" + "SingleExcitation", + "SingleExcitationPlus", + "SingleExcitationMinus" } observables = {"PauliX", "PauliY", "PauliZ", "Hadamard", "Hermitian", "Identity"} diff --git a/pennylane/devices/default_qubit_autograd.py b/pennylane/devices/default_qubit_autograd.py index fc2cf5531ba..bd0ba8d6fad 100644 --- a/pennylane/devices/default_qubit_autograd.py +++ b/pennylane/devices/default_qubit_autograd.py @@ -95,6 +95,9 @@ class DefaultQubitAutograd(DefaultQubit): "CRZ": autograd_ops.CRZ, "CRot": autograd_ops.CRot, "MultiRZ": autograd_ops.MultiRZ, + "SingleExcitation": autograd_ops.SingleExcitation, + "SingleExcitationPlus": autograd_ops.SingleExcitationPlus, + "SingleExcitationMinus": autograd_ops.SingleExcitationMinus, } C_DTYPE = np.complex128 diff --git a/pennylane/devices/default_qubit_jax.py b/pennylane/devices/default_qubit_jax.py index cd51195aa59..e72f307ebd9 100644 --- a/pennylane/devices/default_qubit_jax.py +++ b/pennylane/devices/default_qubit_jax.py @@ -145,6 +145,9 @@ def circuit(): "CRY": jax_ops.CRY, "CRZ": jax_ops.CRZ, "MultiRZ": jax_ops.MultiRZ, + "SingleExcitation": jax_ops.SingleExcitation, + "SingleExcitationPlus": jax_ops.SingleExcitationPlus, + "SingleExcitationMinus": jax_ops.SingleExcitationMinus, } C_DTYPE = jnp.complex64 diff --git a/pennylane/devices/default_qubit_tf.py b/pennylane/devices/default_qubit_tf.py index c80f20e0e94..60ae2481d1f 100644 --- a/pennylane/devices/default_qubit_tf.py +++ b/pennylane/devices/default_qubit_tf.py @@ -143,6 +143,9 @@ class DefaultQubitTF(DefaultQubit): "CRY": tf_ops.CRY, "CRZ": tf_ops.CRZ, "CRot": tf_ops.CRot, + "SingleExcitation": tf_ops.SingleExcitation, + "SingleExcitationPlus": tf_ops.SingleExcitationPlus, + "SingleExcitationMinus": tf_ops.SingleExcitationMinus, } C_DTYPE = tf.complex128 From 5f12bbce4600332aa0fa1b58eb07b37bded8877a Mon Sep 17 00:00:00 2001 From: ixfoduap Date: Wed, 3 Mar 2021 15:46:51 -0500 Subject: [PATCH 04/50] Adds name to contributors --- .github/CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 63d77127e7d..06f3b4f33c2 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -258,8 +258,8 @@ This release contains contributions from (in alphabetical order): -Thomas Bromley, Kyle Godbey, Diego Guala, Josh Izaac, Daniel Polatajko, Chase Roberts, -Sankalp Sanand, Maria Schuld. +Juan Miguel Arrazola, Thomas Bromley, Kyle Godbey, Diego Guala, Josh Izaac, Daniel Polatajko, Chase +Roberts, Sankalp Sanand, Maria Schuld. # Release 0.14.1 (current release) From 6382dc1794c40de10d2f099e7fc5a93fa5961352 Mon Sep 17 00:00:00 2001 From: ixfoduap Date: Wed, 3 Mar 2021 15:49:09 -0500 Subject: [PATCH 05/50] Black --- pennylane/ops/qubit.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pennylane/ops/qubit.py b/pennylane/ops/qubit.py index b24a175ab5d..64615836c8c 100644 --- a/pennylane/ops/qubit.py +++ b/pennylane/ops/qubit.py @@ -1565,10 +1565,12 @@ def decomposition(theta, phi, lam, wires): ] return decomp_ops + # ============================================================================= # Quantum chemistry # ============================================================================= + class SingleExcitation(Operation): r""" Single excitation rotation. @@ -1625,8 +1627,10 @@ def _matrix(cls, *params): @staticmethod def decomposition(theta, wires): - decomp_ops = [SingleExcitationPlus(theta / 2, wires=wires), - SingleExcitationMinus(theta / 2, wires=wires)] + decomp_ops = [ + SingleExcitationPlus(theta / 2, wires=wires), + SingleExcitationMinus(theta / 2, wires=wires), + ] return decomp_ops @@ -2126,7 +2130,7 @@ def diagonalizing_gates(self): "G1Yminus", "G2Y", "G2Yplus", - "G2Yminus" + "G2Yminus", } From 976987503747dbef774eb2c28d5b9b50d3c1b9c2 Mon Sep 17 00:00:00 2001 From: ixfoduap <40441298+ixfoduap@users.noreply.github.com> Date: Wed, 3 Mar 2021 15:57:59 -0500 Subject: [PATCH 06/50] Update .github/CHANGELOG.md --- .github/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 06f3b4f33c2..a8edb555d07 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -3,7 +3,7 @@

New features since last release

- Added the `SingleExcitation` two-qubit operation, which is particularly useful for quantum - chemistry applications. [(#1095)](https://github.com/PennyLaneAI/pennylane/pull/1095) + chemistry applications. [(#1121)](https://github.com/PennyLaneAI/pennylane/pull/1121) It can be used to perform an :math:`SO(2)` rotation in the subspace spanned by the states :math:`|01\rangle, |10\rangle`. For example, the following circuit performs the transformation :math:`|10>\rightarrow \cos(\phi/2)|10\rangle -\sin(\phi/2)|01\rangle)`: From 0521cd2fd8a86975597b056f779fd0b09d0eb4e5 Mon Sep 17 00:00:00 2001 From: ixfoduap Date: Wed, 3 Mar 2021 16:00:34 -0500 Subject: [PATCH 07/50] small changes --- pennylane/devices/default_qubit.py | 2 +- pennylane/devices/jax_ops.py | 2 +- pennylane/devices/tf_ops.py | 2 +- pennylane/ops/qubit.py | 9 +++------ 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/pennylane/devices/default_qubit.py b/pennylane/devices/default_qubit.py index bef36762477..c114f027ddf 100644 --- a/pennylane/devices/default_qubit.py +++ b/pennylane/devices/default_qubit.py @@ -126,7 +126,7 @@ class DefaultQubit(QubitDevice): "QFT", "SingleExcitation", "SingleExcitationPlus", - "SingleExcitationMinus" + "SingleExcitationMinus", } observables = {"PauliX", "PauliY", "PauliZ", "Hadamard", "Hermitian", "Identity"} diff --git a/pennylane/devices/jax_ops.py b/pennylane/devices/jax_ops.py index 5dbd532b843..3951b4ea06c 100644 --- a/pennylane/devices/jax_ops.py +++ b/pennylane/devices/jax_ops.py @@ -229,4 +229,4 @@ def SingleExcitationMinus(phi): c = jnp.cos(phi / 2) s = jnp.sin(phi / 2) e = jnp.exp(-1j * phi / 2) - return jnp.array([[e, 0, 0, 0], [0, c, -s, 0], [0, s, c, 0], [0, 0, 0, e]]) \ No newline at end of file + return jnp.array([[e, 0, 0, 0], [0, c, -s, 0], [0, s, c, 0], [0, 0, 0, e]]) diff --git a/pennylane/devices/tf_ops.py b/pennylane/devices/tf_ops.py index 6b79ceb3d5d..e954cfe41e7 100644 --- a/pennylane/devices/tf_ops.py +++ b/pennylane/devices/tf_ops.py @@ -243,4 +243,4 @@ def SingleExcitationMinus(phi): c = tf.cos(phi / 2) s = tf.sin(phi / 2) e = tf.exp(-1j * phi / 2) - return tf.convert_to_tensor([[e, 0, 0, 0], [0, c, -s, 0], [0, s, c, 0], [0, 0, 0, e]]) \ No newline at end of file + return tf.convert_to_tensor([[e, 0, 0, 0], [0, c, -s, 0], [0, s, c, 0], [0, 0, 0, e]]) diff --git a/pennylane/ops/qubit.py b/pennylane/ops/qubit.py index 64615836c8c..570997b630f 100644 --- a/pennylane/ops/qubit.py +++ b/pennylane/ops/qubit.py @@ -2125,12 +2125,9 @@ def diagonalizing_gates(self): "ControlledQubitUnitary", "DiagonalQubitUnitary", "QFT", - "G1Y", - "G1Yplus", - "G1Yminus", - "G2Y", - "G2Yplus", - "G2Yminus", + "SingleExcitation", + "SingleExcitationPlus", + "SingleExcitationMinus", } From ae018d56e61cdc505c2a2940699826dc3637726b Mon Sep 17 00:00:00 2001 From: ixfoduap <40441298+ixfoduap@users.noreply.github.com> Date: Thu, 4 Mar 2021 09:41:36 -0500 Subject: [PATCH 08/50] Apply suggestions from code review Co-authored-by: Josh Izaac --- .github/CHANGELOG.md | 1 + pennylane/ops/qubit.py | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index a8edb555d07..ed27de31d3d 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -4,6 +4,7 @@ - Added the `SingleExcitation` two-qubit operation, which is particularly useful for quantum chemistry applications. [(#1121)](https://github.com/PennyLaneAI/pennylane/pull/1121) + It can be used to perform an :math:`SO(2)` rotation in the subspace spanned by the states :math:`|01\rangle, |10\rangle`. For example, the following circuit performs the transformation :math:`|10>\rightarrow \cos(\phi/2)|10\rangle -\sin(\phi/2)|01\rangle)`: diff --git a/pennylane/ops/qubit.py b/pennylane/ops/qubit.py index 570997b630f..a853a7f87bf 100644 --- a/pennylane/ops/qubit.py +++ b/pennylane/ops/qubit.py @@ -1572,7 +1572,7 @@ def decomposition(theta, phi, lam, wires): class SingleExcitation(Operation): - r""" + r"""SingleExcitation(phi, wires) Single excitation rotation. .. math:: U(\phi) = \begin{bmatrix} @@ -1635,7 +1635,7 @@ def decomposition(theta, wires): class SingleExcitationMinus(Operation): - r""" + r"""SingleExcitationMinus(phi, wires) Single excitation rotation with negative phase-shift outside the rotation subspace. .. math:: U_-(\phi) = \begin{bmatrix} @@ -1675,7 +1675,7 @@ def _matrix(cls, *params): class SingleExcitationPlus(Operation): - r""" + r"""SingleExcitationPlus(phi, wires) Single excitation rotation with positive phase-shift outside the rotation subspace. .. math:: U_+(\phi) = \begin{bmatrix} From 23e85edc2dce8658f6f800c3e1642b2761af5d57 Mon Sep 17 00:00:00 2001 From: ixfoduap Date: Thu, 4 Mar 2021 14:25:56 -0500 Subject: [PATCH 09/50] Adds tests and improves docstrings --- pennylane/ops/qubit.py | 28 ++++++------- tests/gate_data.py | 42 +++++++++++++++++++ tests/ops/test_qubit_ops.py | 82 ++++++++++++++++++++++++++++++++++++- 3 files changed, 135 insertions(+), 17 deletions(-) diff --git a/pennylane/ops/qubit.py b/pennylane/ops/qubit.py index 570997b630f..812516a477b 100644 --- a/pennylane/ops/qubit.py +++ b/pennylane/ops/qubit.py @@ -1583,28 +1583,28 @@ class SingleExcitation(Operation): \end{bmatrix}. This operation performs a rotation in the two-dimensional subspace :math:`\{|01\rangle, - |10\rangle\}`. + |10\rangle\}`. The name originates from a fermionic interpretation, where the transformation + from :math:`|10\rangle` to `:math:`|01\rangle` is interpreted as "exciting" a particle from + the first qubit to the second. **Details:** * Number of wires: 2 * Number of parameters: 1 - * Gradient recipe: Obtained from its decomposition in terms of the ``SingleExcitationPlus`` and - ``SingleExcitationMinus`` operations + * Gradient recipe: Obtained from its decomposition in terms of the + :class:`~.SingleExcitationPlus` and `:class:`~.SingleExcitationMinus` operations Args: phi (float): rotation angle :math:`\phi` - wires (Sequence[int] or int): the wires the operation acts on + wires (Sequence[int]): the wires the operation acts on **Example** - The following circuit performs the transformation :math:`|10>\rightarrow \cos( + The following circuit performs the transformation :math:`|10\rangle\rightarrow \cos( \phi/2)|10\rangle -\sin(\phi/2)|01\rangle)`: .. code-block:: - dev = qml.device('default.qubit', wires=2) - @qml.qnode(dev) def circuit(phi): qml.PauliX(wires=0) @@ -1649,9 +1649,8 @@ class SingleExcitationMinus(Operation): * Number of wires: 2 * Number of parameters: 1 - * Gradient recipe: :math:`\frac{d}{d\phi}f(R_y(\phi)) = U_-\frac{1}{2}\left[f(U_+(\phi+\pi/2)) - - f(U_-(\phi-\pi/2))\right]` where :math:`f` is an expectation value depending on :math:`U_-( - \phi)`. + * Gradient recipe: :math:`\frac{d}{d\phi}f(R_y(\phi)) = U_-\frac{1}{2}\left[f(U_+(\phi+\pi/2)) - f(U_-(\phi-\pi/2))\right]` + where :math:`f` is an expectation value depending on :math:`U_-(\phi)`. Args: phi (float): rotation angle :math:`\phi` @@ -1669,7 +1668,7 @@ def _matrix(cls, *params): theta = params[0] c = math.cos(theta / 2) s = math.sin(theta / 2) - e = math.exp(-1j * theta / 2) + e = cmath.exp(-1j * theta / 2) return np.array([[e, 0, 0, 0], [0, c, -s, 0], [0, s, c, 0], [0, 0, 0, e]]) @@ -1689,9 +1688,8 @@ class SingleExcitationPlus(Operation): * Number of wires: 2 * Number of parameters: 1 - * Gradient recipe: :math:`\frac{d}{d\phi}f(R_y(\phi)) = U_+\frac{1}{2}\left[f(U_+(\phi+\pi/2)) - - f(U_+(\phi-\pi/2))\right]` where :math:`f` is an expectation value depending on - :math:`U_+(\phi)`. + * Gradient recipe: :math:`\frac{d}{d\phi}f(R_y(\phi)) = U_+\frac{1}{2}\left[f(U_+(\phi+\pi/2)) - f(U_+(\phi-\pi/2))\right]` + where :math:`f` is an expectation value depending on :math:`U_+(\phi)`. Args: phi (float): rotation angle :math:`\phi` @@ -1709,7 +1707,7 @@ def _matrix(cls, *params): theta = params[0] c = math.cos(theta / 2) s = math.sin(theta / 2) - e = math.exp(1j * theta / 2) + e = cmath.exp(1j * theta / 2) return np.array([[e, 0, 0, 0], [0, c, -s, 0], [0, s, c, 0], [0, 0, 0, e]]) diff --git a/tests/gate_data.py b/tests/gate_data.py index 752b3a2a26f..96331594845 100644 --- a/tests/gate_data.py +++ b/tests/gate_data.py @@ -233,3 +233,45 @@ def ControlledPhaseShift(phi): array: the two-wire controlled-phase matrix """ return np.diag([1, 1, 1, np.exp(1j * phi)]) + + +def SingleExcitation(phi): + r"""Single excitation rotation. + + Args: + phi (float): rotation angle + + Returns: + array: the two-qubit Givens rotation describing the single excitation + """ + + return np.array([[1, 0, 0, 0], [0, np.cos(phi/2), -np.sin(phi/2), 0], + [0, np.sin(phi/2), np.cos(phi/2), 0], [0, 0, 0, 1]]) + + +def SingleExcitationPlus(phi): + r"""Single excitation rotation with positive phase shift. + + Args: + phi (float): rotation angle + + Returns: + array: the two-qubit matrix describing the operation + """ + + return np.array([[np.exp(1j*phi/2), 0, 0, 0], [0, np.cos(phi/2), -np.sin(phi/2), 0], + [0, np.sin(phi/2), np.cos(phi/2), 0], [0, 0, 0, np.exp(1j*phi/2)]]) + + +def SingleExcitationMinus(phi): + r"""Single excitation rotation with negative phase shift. + + Args: + phi (float): rotation angle + + Returns: + array: the two-qubit matrix describing the operation + """ + + return np.array([[np.exp(-1j*phi/2), 0, 0, 0], [0, np.cos(phi/2), -np.sin(phi/2), 0], + [0, np.sin(phi/2), np.cos(phi/2), 0], [0, 0, 0, np.exp(-1j*phi/2)]]) diff --git a/tests/ops/test_qubit_ops.py b/tests/ops/test_qubit_ops.py index 3e28288f57c..096bdc25657 100644 --- a/tests/ops/test_qubit_ops.py +++ b/tests/ops/test_qubit_ops.py @@ -19,14 +19,16 @@ import numpy as np from numpy.linalg import multi_dot from scipy.stats import unitary_group +from scipy.linalg import expm import pennylane as qml from pennylane.wires import Wires -from gate_data import I, X, Y, Z, H, CNOT, SWAP, CZ, S, T, CSWAP, Toffoli, QFT, ControlledPhaseShift +from gate_data import I, X, Y, Z, H, CNOT, SWAP, CZ, S, T, CSWAP, Toffoli, QFT, \ + ControlledPhaseShift, SingleExcitation, SingleExcitationPlus, SingleExcitationMinus -# Standard observables, their matrix representation, and eigenvlaues +# Standard observables, their matrix representation, and eigenvalues OBSERVABLES = [ (qml.PauliX, X, [1, -1]), (qml.PauliY, Y, [1, -1]), @@ -800,6 +802,82 @@ def test_controlled_phase_shift_decomp(self, phi): assert np.allclose(decomposed_matrix, exp) + @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi/4]) + def test_single_excitation_matrix(self, phi): + """Tests that the SingleExcitation operation calculates the correct matrix""" + op = qml.SingleExcitation(phi, wires=[0, 1]) + res = op.matrix + exp = SingleExcitation(phi) + assert np.allclose(res, exp) + + @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi/4]) + def test_single_excitation_decomp(self, phi): + """Tests that the SingleExcitation operation calculates the correct decomposition""" + op = qml.SingleExcitation(phi, wires=[0, 1]) + decomp = op.decomposition(phi, wires=[0, 1]) + + mats = [m.matrix for m in decomp] + + decomposed_matrix = mats[0] @ mats[1] + exp = SingleExcitation(phi) + + assert np.allclose(decomposed_matrix, exp) + + @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi/4]) + def test_single_excitation_generator(self, phi): + """Tests that the SingleExcitation operation calculates the correct generator""" + op = qml.SingleExcitation(phi, wires=[0, 1]) + g, a = op.generator + print('g= ', g) + print('a= ', a) + res = expm(1j * a * g * phi) + exp = SingleExcitation(phi) + print('res=', res) + print('exp=', exp) + assert np.allclose(res, exp) + + @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi/4]) + def test_single_excitation_plus_matrix(self, phi): + """Tests that the SingleExcitationPlus operation calculates the correct matrix""" + op = qml.SingleExcitationPlus(phi, wires=[0, 1]) + res = op.matrix + exp = SingleExcitationPlus(phi) + assert np.allclose(res, exp) + + @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi/4]) + def test_single_excitation_plus_generator(self, phi): + """Tests that the SingleExcitationPlus operation calculates the correct generator""" + op = qml.SingleExcitationPlus(phi, wires=[0, 1]) + g, a = op.generator + print('g= ', g) + print('a= ', a) + res = expm(1j * a * g * phi) + exp = SingleExcitationPlus(phi) + print('res=', res) + print('exp=', exp) + assert np.allclose(res, exp) + + @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi/4]) + def test_single_excitation_minus_matrix(self, phi): + """Tests that the SingleExcitationPlus operation calculates the correct matrix""" + op = qml.SingleExcitationMinus(phi, wires=[0, 1]) + res = op.matrix + exp = SingleExcitationMinus(phi) + assert np.allclose(res, exp) + + @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi/4]) + def test_single_excitation_minus_generator(self, phi): + """Tests that the SingleExcitationPlus operation calculates the correct generator""" + op = qml.SingleExcitationMinus(phi, wires=[0, 1]) + g, a = op.generator + print('g= ', g) + print('a= ', a) + res = expm(1j * a * g * phi) + exp = SingleExcitationMinus(phi) + print('res=', res) + print('exp=', exp) + assert np.allclose(res, exp) + PAULI_ROT_PARAMETRIC_MATRIX_TEST_DATA = [ ( From 882bb77aa6fa2c21330a9a716550a35f0aff00d7 Mon Sep 17 00:00:00 2001 From: ixfoduap <40441298+ixfoduap@users.noreply.github.com> Date: Thu, 4 Mar 2021 14:27:37 -0500 Subject: [PATCH 10/50] Update .github/CHANGELOG.md --- .github/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index ed27de31d3d..cd771eab8eb 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -2,7 +2,7 @@

New features since last release

-- Added the `SingleExcitation` two-qubit operation, which is particularly useful for quantum +- Added the `SingleExcitation` two-qubit operation, which is useful for quantum chemistry applications. [(#1121)](https://github.com/PennyLaneAI/pennylane/pull/1121) It can be used to perform an :math:`SO(2)` rotation in the subspace From 40b2320650a4f8449e13e8e4957cb396fa8fa7fe Mon Sep 17 00:00:00 2001 From: ixfoduap <40441298+ixfoduap@users.noreply.github.com> Date: Thu, 4 Mar 2021 14:38:31 -0500 Subject: [PATCH 11/50] Update .github/CHANGELOG.md --- .github/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index cd771eab8eb..ecb4338548f 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -7,7 +7,7 @@ It can be used to perform an :math:`SO(2)` rotation in the subspace spanned by the states :math:`|01\rangle, |10\rangle`. For example, the following circuit - performs the transformation :math:`|10>\rightarrow \cos(\phi/2)|10\rangle -\sin(\phi/2)|01\rangle)`: + performs the transformation :math:`|10\rangle\rightarrow \cos(\phi/2)|10\rangle -\sin(\phi/2)|01\rangle)`: ```python dev = qml.device('default.qubit', wires=2) From 3e9f7baef519d8d58fb38a1af9b2f5f5f2544f33 Mon Sep 17 00:00:00 2001 From: ixfoduap <40441298+ixfoduap@users.noreply.github.com> Date: Thu, 4 Mar 2021 14:57:11 -0500 Subject: [PATCH 12/50] Apply suggestions from code review --- pennylane/devices/autograd_ops.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pennylane/devices/autograd_ops.py b/pennylane/devices/autograd_ops.py index 49b73334b0a..e3163de245b 100644 --- a/pennylane/devices/autograd_ops.py +++ b/pennylane/devices/autograd_ops.py @@ -190,7 +190,8 @@ def SingleExcitation(phi): phi (float): rotation angle Returns: - jnp.Tensor[complex]: Single excitation rotation matrix + array[complex]: Single excitation rotation matrix + """ c = np.cos(phi / 2) @@ -206,7 +207,8 @@ def SingleExcitationPlus(phi): phi (float): rotation angle Returns: - jnp.Tensor[complex]: Single excitation rotation matrix with positive phase-shift + array[complex]: Single excitation rotation matrix with positive phase-shift + """ c = np.cos(phi / 2) @@ -223,7 +225,8 @@ def SingleExcitationMinus(phi): phi (float): rotation angle Returns: - tf.Tensor[complex]: Single excitation rotation matrix with negative phase-shift + array[complex]: Single excitation rotation matrix with negative phase-shift + """ c = np.cos(phi / 2) From 62683a12b8f61abfe4ec2eeb2f88cd77549c1bb2 Mon Sep 17 00:00:00 2001 From: ixfoduap Date: Thu, 4 Mar 2021 16:52:40 -0500 Subject: [PATCH 13/50] excludes ops from reversible tests --- tests/tape/tapes/test_reversible.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/tape/tapes/test_reversible.py b/tests/tape/tapes/test_reversible.py index 2486076790b..81b4685638e 100644 --- a/tests/tape/tapes/test_reversible.py +++ b/tests/tape/tapes/test_reversible.py @@ -286,6 +286,9 @@ def test_multiple_rx_gradient(self, tol): qml.U1, qml.U2, qml.U3, + qml.SingleExcitation, + qml.SingleExcitationPlus, + qml.SingleExcitationMinus, } @pytest.mark.parametrize("obs", [qml.PauliX, qml.PauliY]) From ced73be772a96bbecfd8b1da9d859d56323840f5 Mon Sep 17 00:00:00 2001 From: ixfoduap Date: Tue, 9 Mar 2021 16:55:01 -0500 Subject: [PATCH 14/50] Adds test of ops across interfaces --- pennylane/devices/tests/test_ops.py | 107 ++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 pennylane/devices/tests/test_ops.py diff --git a/pennylane/devices/tests/test_ops.py b/pennylane/devices/tests/test_ops.py new file mode 100644 index 00000000000..e7f4da3fc83 --- /dev/null +++ b/pennylane/devices/tests/test_ops.py @@ -0,0 +1,107 @@ +# Copyright 2018-2021 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License 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. + +""" +Tests operations defined across different interfaces +""" + +import pennylane as qml +from pennylane.devices import jax_ops, tf_ops, autograd_ops +import numpy as np +import jax.numpy as np +import pytest + + +class TestJaxOps: + """ + Tests that the matrix defining jax operations matches the intended one + """ + + @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) + def test_single_excitation(self, phi): + + jx_mat = jax_ops.SingleExcitation(phi) + m = qml.SingleExcitation(phi, wires=[0, 1]) + + assert np.allclose(jx_mat, m.matrix) + + @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) + def test_single_excitation_plus(self, phi): + jx_mat = jax_ops.SingleExcitationPlus(phi) + m = qml.SingleExcitationPlus(phi, wires=[0, 1]) + + assert np.allclose(jx_mat, m.matrix) + + @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) + def test_single_excitation_minus(self, phi): + jx_mat = jax_ops.SingleExcitationMinus(phi) + m = qml.SingleExcitationMinus(phi, wires=[0, 1]) + + assert np.allclose(jx_mat, m.matrix) + + +class TestAutogradOps: + """ + Tests that the matrix defining autograd operations matches the intended one + """ + + @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) + def test_single_excitation(self, phi): + + a_mat = autograd_ops.SingleExcitation(phi) + m = qml.SingleExcitation(phi, wires=[0, 1]) + + assert np.allclose(a_mat, m.matrix) + + @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) + def test_single_excitation_plus(self, phi): + a_mat = autograd_ops.SingleExcitationPlus(phi) + m = qml.SingleExcitationPlus(phi, wires=[0, 1]) + + assert np.allclose(a_mat, m.matrix) + + @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) + def test_single_excitation_minus(self, phi): + a_mat = autograd_ops.SingleExcitationMinus(phi) + m = qml.SingleExcitationMinus(phi, wires=[0, 1]) + + assert np.allclose(a_mat, m.matrix) + + +class TestTFOps: + """ + Tests that the matrix defining tensorflow operations matches the intended one + """ + + @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) + def test_single_excitation(self, phi): + + tf_mat = tf_ops.SingleExcitation(phi) + m = qml.SingleExcitation(phi, wires=[0, 1]) + + assert np.allclose(tf_mat, m.matrix) + + @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) + def test_single_excitation_plus(self, phi): + tf_mat = tf_ops.SingleExcitationPlus(phi) + m = qml.SingleExcitationPlus(phi, wires=[0, 1]) + + assert np.allclose(tf_mat, m.matrix) + + @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) + def test_single_excitation_minus(self, phi): + tf_mat = tf_ops.SingleExcitationMinus(phi) + m = qml.SingleExcitationMinus(phi, wires=[0, 1]) + + assert np.allclose(tf_mat, m.matrix) From 6c7bc216a6274652849beadffc24a3d8b4f26b8e Mon Sep 17 00:00:00 2001 From: ixfoduap Date: Wed, 10 Mar 2021 13:55:20 -0500 Subject: [PATCH 15/50] adds docstrings and ignore no-self-use --- pennylane/devices/tests/test_ops.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/pennylane/devices/tests/test_ops.py b/pennylane/devices/tests/test_ops.py index e7f4da3fc83..b812aee8f37 100644 --- a/pennylane/devices/tests/test_ops.py +++ b/pennylane/devices/tests/test_ops.py @@ -22,6 +22,8 @@ import jax.numpy as np import pytest +# pylint: disable=R0201 + class TestJaxOps: """ @@ -30,7 +32,7 @@ class TestJaxOps: @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) def test_single_excitation(self, phi): - + """Test correctness of SingleExcitation""" jx_mat = jax_ops.SingleExcitation(phi) m = qml.SingleExcitation(phi, wires=[0, 1]) @@ -38,6 +40,7 @@ def test_single_excitation(self, phi): @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) def test_single_excitation_plus(self, phi): + """Test correctness of SingleExcitationPlus""" jx_mat = jax_ops.SingleExcitationPlus(phi) m = qml.SingleExcitationPlus(phi, wires=[0, 1]) @@ -45,6 +48,7 @@ def test_single_excitation_plus(self, phi): @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) def test_single_excitation_minus(self, phi): + """Test correctness of SingleExcitationMinus""" jx_mat = jax_ops.SingleExcitationMinus(phi) m = qml.SingleExcitationMinus(phi, wires=[0, 1]) @@ -58,7 +62,7 @@ class TestAutogradOps: @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) def test_single_excitation(self, phi): - + """Test correctness of SingleExcitation""" a_mat = autograd_ops.SingleExcitation(phi) m = qml.SingleExcitation(phi, wires=[0, 1]) @@ -66,6 +70,7 @@ def test_single_excitation(self, phi): @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) def test_single_excitation_plus(self, phi): + """Test correctness of SingleExcitationPlus""" a_mat = autograd_ops.SingleExcitationPlus(phi) m = qml.SingleExcitationPlus(phi, wires=[0, 1]) @@ -73,6 +78,7 @@ def test_single_excitation_plus(self, phi): @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) def test_single_excitation_minus(self, phi): + """Test correctness of SingleExcitationMinus""" a_mat = autograd_ops.SingleExcitationMinus(phi) m = qml.SingleExcitationMinus(phi, wires=[0, 1]) @@ -86,7 +92,7 @@ class TestTFOps: @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) def test_single_excitation(self, phi): - + """Test correctness of SingleExcitation""" tf_mat = tf_ops.SingleExcitation(phi) m = qml.SingleExcitation(phi, wires=[0, 1]) @@ -94,6 +100,7 @@ def test_single_excitation(self, phi): @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) def test_single_excitation_plus(self, phi): + """Test correctness of SingleExcitationPlus""" tf_mat = tf_ops.SingleExcitationPlus(phi) m = qml.SingleExcitationPlus(phi, wires=[0, 1]) @@ -101,6 +108,7 @@ def test_single_excitation_plus(self, phi): @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) def test_single_excitation_minus(self, phi): + """Test correctness of SingleExcitationMinus""" tf_mat = tf_ops.SingleExcitationMinus(phi) m = qml.SingleExcitationMinus(phi, wires=[0, 1]) From d8343c227248e87f95eab53a3df254d43d90c09f Mon Sep 17 00:00:00 2001 From: antalszava Date: Wed, 10 Mar 2021 15:04:44 -0500 Subject: [PATCH 16/50] jax interface for mixed --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index befd6f04bcf..8137912007b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -87,7 +87,7 @@ jobs: # - {device: "default.qubit", analytic: false, shots: 1000} # - {device: "default.qubit.tf", analytic: true, shots: 1000} - {device: "default.qubit.autograd", analytic: true, shots: 1000} - - {device: "default.mixed", analytic: true, shots: 1000} + - {device: "default.mixed", analytic: true, shots: 1000, interfaces: ['jax']} steps: - name: Cancel Previous Runs From 0744aaa7242806013d1db7b37667ae0c8ff51138 Mon Sep 17 00:00:00 2001 From: ixfoduap <40441298+ixfoduap@users.noreply.github.com> Date: Wed, 10 Mar 2021 16:27:06 -0500 Subject: [PATCH 17/50] Update pennylane/devices/tests/test_ops.py --- pennylane/devices/tests/test_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/devices/tests/test_ops.py b/pennylane/devices/tests/test_ops.py index b812aee8f37..819200ab972 100644 --- a/pennylane/devices/tests/test_ops.py +++ b/pennylane/devices/tests/test_ops.py @@ -19,7 +19,7 @@ import pennylane as qml from pennylane.devices import jax_ops, tf_ops, autograd_ops import numpy as np -import jax.numpy as np +import jax.numpy as jnp import pytest # pylint: disable=R0201 From aeb675141b512e60d5c5ccb803d36aecc53dc52f Mon Sep 17 00:00:00 2001 From: ixfoduap Date: Wed, 10 Mar 2021 16:39:51 -0500 Subject: [PATCH 18/50] Corrects issues found by checks --- pennylane/devices/tests/test_ops.py | 115 ---------------------------- pennylane/ops/qubit.py | 7 +- tests/ops/test_qubit_ops.py | 89 ++++++++++++++++++--- 3 files changed, 81 insertions(+), 130 deletions(-) delete mode 100644 pennylane/devices/tests/test_ops.py diff --git a/pennylane/devices/tests/test_ops.py b/pennylane/devices/tests/test_ops.py deleted file mode 100644 index b812aee8f37..00000000000 --- a/pennylane/devices/tests/test_ops.py +++ /dev/null @@ -1,115 +0,0 @@ -# Copyright 2018-2021 Xanadu Quantum Technologies Inc. - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License 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. - -""" -Tests operations defined across different interfaces -""" - -import pennylane as qml -from pennylane.devices import jax_ops, tf_ops, autograd_ops -import numpy as np -import jax.numpy as np -import pytest - -# pylint: disable=R0201 - - -class TestJaxOps: - """ - Tests that the matrix defining jax operations matches the intended one - """ - - @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) - def test_single_excitation(self, phi): - """Test correctness of SingleExcitation""" - jx_mat = jax_ops.SingleExcitation(phi) - m = qml.SingleExcitation(phi, wires=[0, 1]) - - assert np.allclose(jx_mat, m.matrix) - - @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) - def test_single_excitation_plus(self, phi): - """Test correctness of SingleExcitationPlus""" - jx_mat = jax_ops.SingleExcitationPlus(phi) - m = qml.SingleExcitationPlus(phi, wires=[0, 1]) - - assert np.allclose(jx_mat, m.matrix) - - @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) - def test_single_excitation_minus(self, phi): - """Test correctness of SingleExcitationMinus""" - jx_mat = jax_ops.SingleExcitationMinus(phi) - m = qml.SingleExcitationMinus(phi, wires=[0, 1]) - - assert np.allclose(jx_mat, m.matrix) - - -class TestAutogradOps: - """ - Tests that the matrix defining autograd operations matches the intended one - """ - - @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) - def test_single_excitation(self, phi): - """Test correctness of SingleExcitation""" - a_mat = autograd_ops.SingleExcitation(phi) - m = qml.SingleExcitation(phi, wires=[0, 1]) - - assert np.allclose(a_mat, m.matrix) - - @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) - def test_single_excitation_plus(self, phi): - """Test correctness of SingleExcitationPlus""" - a_mat = autograd_ops.SingleExcitationPlus(phi) - m = qml.SingleExcitationPlus(phi, wires=[0, 1]) - - assert np.allclose(a_mat, m.matrix) - - @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) - def test_single_excitation_minus(self, phi): - """Test correctness of SingleExcitationMinus""" - a_mat = autograd_ops.SingleExcitationMinus(phi) - m = qml.SingleExcitationMinus(phi, wires=[0, 1]) - - assert np.allclose(a_mat, m.matrix) - - -class TestTFOps: - """ - Tests that the matrix defining tensorflow operations matches the intended one - """ - - @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) - def test_single_excitation(self, phi): - """Test correctness of SingleExcitation""" - tf_mat = tf_ops.SingleExcitation(phi) - m = qml.SingleExcitation(phi, wires=[0, 1]) - - assert np.allclose(tf_mat, m.matrix) - - @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) - def test_single_excitation_plus(self, phi): - """Test correctness of SingleExcitationPlus""" - tf_mat = tf_ops.SingleExcitationPlus(phi) - m = qml.SingleExcitationPlus(phi, wires=[0, 1]) - - assert np.allclose(tf_mat, m.matrix) - - @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) - def test_single_excitation_minus(self, phi): - """Test correctness of SingleExcitationMinus""" - tf_mat = tf_ops.SingleExcitationMinus(phi) - m = qml.SingleExcitationMinus(phi, wires=[0, 1]) - - assert np.allclose(tf_mat, m.matrix) diff --git a/pennylane/ops/qubit.py b/pennylane/ops/qubit.py index 681778e090d..af75120e958 100644 --- a/pennylane/ops/qubit.py +++ b/pennylane/ops/qubit.py @@ -1687,12 +1687,13 @@ class SingleExcitation(Operation): * Number of wires: 2 * Number of parameters: 1 * Gradient recipe: Obtained from its decomposition in terms of the - :class:`~.SingleExcitationPlus` and `:class:`~.SingleExcitationMinus` operations + :class:`~.SingleExcitationPlus` and `:class:`~.SingleExcitationMinus` operations Args: phi (float): rotation angle :math:`\phi` wires (Sequence[int]): the wires the operation acts on + **Example** The following circuit performs the transformation :math:`|10\rangle\rightarrow \cos( @@ -1745,7 +1746,7 @@ class SingleExcitationMinus(Operation): * Number of wires: 2 * Number of parameters: 1 * Gradient recipe: :math:`\frac{d}{d\phi}f(R_y(\phi)) = U_-\frac{1}{2}\left[f(U_+(\phi+\pi/2)) - f(U_-(\phi-\pi/2))\right]` - where :math:`f` is an expectation value depending on :math:`U_-(\phi)`. + where :math:`f` is an expectation value depending on :math:`U_-(\phi)`. Args: phi (float): rotation angle :math:`\phi` @@ -1784,7 +1785,7 @@ class SingleExcitationPlus(Operation): * Number of wires: 2 * Number of parameters: 1 * Gradient recipe: :math:`\frac{d}{d\phi}f(R_y(\phi)) = U_+\frac{1}{2}\left[f(U_+(\phi+\pi/2)) - f(U_+(\phi-\pi/2))\right]` - where :math:`f` is an expectation value depending on :math:`U_+(\phi)`. + where :math:`f` is an expectation value depending on :math:`U_+(\phi)`. Args: phi (float): rotation angle :math:`\phi` diff --git a/tests/ops/test_qubit_ops.py b/tests/ops/test_qubit_ops.py index 00355ca993c..c251604483b 100644 --- a/tests/ops/test_qubit_ops.py +++ b/tests/ops/test_qubit_ops.py @@ -20,9 +20,11 @@ from numpy.linalg import multi_dot from scipy.stats import unitary_group from scipy.linalg import expm +import jax.numpy as jnp import pennylane as qml from pennylane.wires import Wires +from pennylane.devices import jax_ops, tf_ops, autograd_ops from gate_data import I, X, Y, Z, H, CNOT, SWAP, CZ, S, T, CSWAP, Toffoli, QFT, \ ControlledPhaseShift, SingleExcitation, SingleExcitationPlus, SingleExcitationMinus @@ -866,6 +868,9 @@ def test_controlled_phase_shift_decomp(self, phi): assert np.allclose(decomposed_matrix, exp) + +class TestSingleExcitation: + @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi/4]) def test_single_excitation_matrix(self, phi): """Tests that the SingleExcitation operation calculates the correct matrix""" @@ -892,12 +897,8 @@ def test_single_excitation_generator(self, phi): """Tests that the SingleExcitation operation calculates the correct generator""" op = qml.SingleExcitation(phi, wires=[0, 1]) g, a = op.generator - print('g= ', g) - print('a= ', a) res = expm(1j * a * g * phi) exp = SingleExcitation(phi) - print('res=', res) - print('exp=', exp) assert np.allclose(res, exp) @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi/4]) @@ -913,12 +914,8 @@ def test_single_excitation_plus_generator(self, phi): """Tests that the SingleExcitationPlus operation calculates the correct generator""" op = qml.SingleExcitationPlus(phi, wires=[0, 1]) g, a = op.generator - print('g= ', g) - print('a= ', a) res = expm(1j * a * g * phi) exp = SingleExcitationPlus(phi) - print('res=', res) - print('exp=', exp) assert np.allclose(res, exp) @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi/4]) @@ -934,14 +931,82 @@ def test_single_excitation_minus_generator(self, phi): """Tests that the SingleExcitationPlus operation calculates the correct generator""" op = qml.SingleExcitationMinus(phi, wires=[0, 1]) g, a = op.generator - print('g= ', g) - print('a= ', a) res = expm(1j * a * g * phi) exp = SingleExcitationMinus(phi) - print('res=', res) - print('exp=', exp) assert np.allclose(res, exp) + @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) + def test_single_excitation_jax(self, phi): + """Test correctness of SingleExcitation jax operation""" + jx_mat = jax_ops.SingleExcitation(phi) + m = qml.SingleExcitation(phi, wires=[0, 1]) + + assert np.allclose(jx_mat, m.matrix) + + @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) + def test_single_excitation_plus_jax(self, phi): + """Test correctness of SingleExcitationPlus jax operation""" + jx_mat = jax_ops.SingleExcitationPlus(phi) + m = qml.SingleExcitationPlus(phi, wires=[0, 1]) + + assert np.allclose(jx_mat, m.matrix) + + @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) + def test_single_excitation_minus_jax(self, phi): + """Test correctness of SingleExcitationMinus jax operation""" + jx_mat = jax_ops.SingleExcitationMinus(phi) + m = qml.SingleExcitationMinus(phi, wires=[0, 1]) + + assert np.allclose(jx_mat, m.matrix) + + @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) + def test_single_excitation_ad(self, phi): + """Test correctness of SingleExcitation autograd operation""" + a_mat = autograd_ops.SingleExcitation(phi) + m = qml.SingleExcitation(phi, wires=[0, 1]) + + assert np.allclose(a_mat, m.matrix) + + @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) + def test_single_excitation_plus_ad(self, phi): + """Test correctness of SingleExcitationPlus autograd operation""" + a_mat = autograd_ops.SingleExcitationPlus(phi) + m = qml.SingleExcitationPlus(phi, wires=[0, 1]) + + assert np.allclose(a_mat, m.matrix) + + @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) + def test_single_excitation_minus_ad(self, phi): + """Test correctness of SingleExcitationMinus autograd operation""" + a_mat = autograd_ops.SingleExcitationMinus(phi) + m = qml.SingleExcitationMinus(phi, wires=[0, 1]) + + assert np.allclose(a_mat, m.matrix) + + @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) + def test_single_excitation_tf(self, phi): + """Test correctness of SingleExcitation tensorflow operation""" + tf_mat = tf_ops.SingleExcitation(phi) + m = qml.SingleExcitation(phi, wires=[0, 1]) + + assert np.allclose(tf_mat, m.matrix) + + @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) + def test_single_excitation_plus_tf(self, phi): + """Test correctness of SingleExcitationPlus tensorflow operation""" + tf_mat = tf_ops.SingleExcitationPlus(phi) + m = qml.SingleExcitationPlus(phi, wires=[0, 1]) + + assert np.allclose(tf_mat, m.matrix) + + @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) + def test_single_excitation_minus_tf(self, phi): + """Test correctness of SingleExcitationMinus tensorflow operation""" + tf_mat = tf_ops.SingleExcitationMinus(phi) + m = qml.SingleExcitationMinus(phi, wires=[0, 1]) + + assert np.allclose(tf_mat, m.matrix) + PAULI_ROT_PARAMETRIC_MATRIX_TEST_DATA = [ ( From e6a00e4efdd3bfe31743dae5414fca8319b2d719 Mon Sep 17 00:00:00 2001 From: ixfoduap Date: Wed, 10 Mar 2021 16:52:05 -0500 Subject: [PATCH 19/50] Removes unnecessary import --- tests/ops/test_qubit_ops.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/ops/test_qubit_ops.py b/tests/ops/test_qubit_ops.py index c251604483b..78ce0a7dfe9 100644 --- a/tests/ops/test_qubit_ops.py +++ b/tests/ops/test_qubit_ops.py @@ -20,7 +20,6 @@ from numpy.linalg import multi_dot from scipy.stats import unitary_group from scipy.linalg import expm -import jax.numpy as jnp import pennylane as qml from pennylane.wires import Wires From 95c450ce30e58708f509751a61f84b3a03c32d5e Mon Sep 17 00:00:00 2001 From: ixfoduap Date: Wed, 10 Mar 2021 17:11:41 -0500 Subject: [PATCH 20/50] Removes jax test that is failing --- tests/ops/test_qubit_ops.py | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/tests/ops/test_qubit_ops.py b/tests/ops/test_qubit_ops.py index 78ce0a7dfe9..7fab0aba85f 100644 --- a/tests/ops/test_qubit_ops.py +++ b/tests/ops/test_qubit_ops.py @@ -23,7 +23,7 @@ import pennylane as qml from pennylane.wires import Wires -from pennylane.devices import jax_ops, tf_ops, autograd_ops +from pennylane.devices import tf_ops, autograd_ops from gate_data import I, X, Y, Z, H, CNOT, SWAP, CZ, S, T, CSWAP, Toffoli, QFT, \ ControlledPhaseShift, SingleExcitation, SingleExcitationPlus, SingleExcitationMinus @@ -934,30 +934,6 @@ def test_single_excitation_minus_generator(self, phi): exp = SingleExcitationMinus(phi) assert np.allclose(res, exp) - @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) - def test_single_excitation_jax(self, phi): - """Test correctness of SingleExcitation jax operation""" - jx_mat = jax_ops.SingleExcitation(phi) - m = qml.SingleExcitation(phi, wires=[0, 1]) - - assert np.allclose(jx_mat, m.matrix) - - @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) - def test_single_excitation_plus_jax(self, phi): - """Test correctness of SingleExcitationPlus jax operation""" - jx_mat = jax_ops.SingleExcitationPlus(phi) - m = qml.SingleExcitationPlus(phi, wires=[0, 1]) - - assert np.allclose(jx_mat, m.matrix) - - @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) - def test_single_excitation_minus_jax(self, phi): - """Test correctness of SingleExcitationMinus jax operation""" - jx_mat = jax_ops.SingleExcitationMinus(phi) - m = qml.SingleExcitationMinus(phi, wires=[0, 1]) - - assert np.allclose(jx_mat, m.matrix) - @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) def test_single_excitation_ad(self, phi): """Test correctness of SingleExcitation autograd operation""" From 1b9121d12d0505b176ed2e10c62f121c93eb82ea Mon Sep 17 00:00:00 2001 From: antalszava Date: Wed, 10 Mar 2021 17:18:18 -0500 Subject: [PATCH 21/50] Update .github/workflows/tests.yml --- .github/workflows/tests.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8137912007b..4a34b759cc8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -87,7 +87,8 @@ jobs: # - {device: "default.qubit", analytic: false, shots: 1000} # - {device: "default.qubit.tf", analytic: true, shots: 1000} - {device: "default.qubit.autograd", analytic: true, shots: 1000} - - {device: "default.mixed", analytic: true, shots: 1000, interfaces: ['jax']} + - {device: "default.mixed", analytic: true, shots: 1000} + steps: - name: Cancel Previous Runs From 08fbb8bb670f68d1fb2c656753120ba78494ec1f Mon Sep 17 00:00:00 2001 From: antalszava Date: Wed, 10 Mar 2021 17:18:38 -0500 Subject: [PATCH 22/50] Update .github/workflows/tests.yml --- .github/workflows/tests.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4a34b759cc8..befd6f04bcf 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -89,7 +89,6 @@ jobs: - {device: "default.qubit.autograd", analytic: true, shots: 1000} - {device: "default.mixed", analytic: true, shots: 1000} - steps: - name: Cancel Previous Runs uses: styfle/cancel-workflow-action@0.4.1 From 0cc729e5c81bc6e71babc545cc1e9149a7809997 Mon Sep 17 00:00:00 2001 From: ixfoduap Date: Thu, 11 Mar 2021 15:55:54 -0500 Subject: [PATCH 23/50] Adds interface tests --- tests/ops/test_qubit_ops.py | 55 ++++++++----------------------------- 1 file changed, 12 insertions(+), 43 deletions(-) diff --git a/tests/ops/test_qubit_ops.py b/tests/ops/test_qubit_ops.py index 7fab0aba85f..12a7bce56ed 100644 --- a/tests/ops/test_qubit_ops.py +++ b/tests/ops/test_qubit_ops.py @@ -934,53 +934,22 @@ def test_single_excitation_minus_generator(self, phi): exp = SingleExcitationMinus(phi) assert np.allclose(res, exp) - @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) - def test_single_excitation_ad(self, phi): - """Test correctness of SingleExcitation autograd operation""" - a_mat = autograd_ops.SingleExcitation(phi) - m = qml.SingleExcitation(phi, wires=[0, 1]) + @pytest.mark.parametrize("plugin", ['autograd', 'tf', 'jax']) + def test_autograd(self, plugin): + """Tests that gradients and operations are computed correctly using the + autograd interface""" - assert np.allclose(a_mat, m.matrix) + dev = qml.device('default.qubit.' + plugin, wires=2) + state = np.array([0, -1/np.sqrt(2), 1/np.sqrt(2), 0]) - @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) - def test_single_excitation_plus_ad(self, phi): - """Test correctness of SingleExcitationPlus autograd operation""" - a_mat = autograd_ops.SingleExcitationPlus(phi) - m = qml.SingleExcitationPlus(phi, wires=[0, 1]) - - assert np.allclose(a_mat, m.matrix) - - @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) - def test_single_excitation_minus_ad(self, phi): - """Test correctness of SingleExcitationMinus autograd operation""" - a_mat = autograd_ops.SingleExcitationMinus(phi) - m = qml.SingleExcitationMinus(phi, wires=[0, 1]) - - assert np.allclose(a_mat, m.matrix) - - @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) - def test_single_excitation_tf(self, phi): - """Test correctness of SingleExcitation tensorflow operation""" - tf_mat = tf_ops.SingleExcitation(phi) - m = qml.SingleExcitation(phi, wires=[0, 1]) - - assert np.allclose(tf_mat, m.matrix) - - @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) - def test_single_excitation_plus_tf(self, phi): - """Test correctness of SingleExcitationPlus tensorflow operation""" - tf_mat = tf_ops.SingleExcitationPlus(phi) - m = qml.SingleExcitationPlus(phi, wires=[0, 1]) - - assert np.allclose(tf_mat, m.matrix) + @qml.qnode(dev) + def circuit(phi): + qml.PauliX(wires=0) + qml.SingleExcitation(phi, wires=[0, 1]) - @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi / 4]) - def test_single_excitation_minus_tf(self, phi): - """Test correctness of SingleExcitationMinus tensorflow operation""" - tf_mat = tf_ops.SingleExcitationMinus(phi) - m = qml.SingleExcitationMinus(phi, wires=[0, 1]) + return qml.state() - assert np.allclose(tf_mat, m.matrix) + assert np.allclose(state, circuit(np.pi/2)) PAULI_ROT_PARAMETRIC_MATRIX_TEST_DATA = [ From 2fedf4c8daafd6459c447497903710f2beebdc39 Mon Sep 17 00:00:00 2001 From: ixfoduap Date: Thu, 11 Mar 2021 16:16:57 -0500 Subject: [PATCH 24/50] removes interface tests --- tests/ops/test_qubit_ops.py | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/tests/ops/test_qubit_ops.py b/tests/ops/test_qubit_ops.py index 12a7bce56ed..24a48d8fd7e 100644 --- a/tests/ops/test_qubit_ops.py +++ b/tests/ops/test_qubit_ops.py @@ -934,23 +934,6 @@ def test_single_excitation_minus_generator(self, phi): exp = SingleExcitationMinus(phi) assert np.allclose(res, exp) - @pytest.mark.parametrize("plugin", ['autograd', 'tf', 'jax']) - def test_autograd(self, plugin): - """Tests that gradients and operations are computed correctly using the - autograd interface""" - - dev = qml.device('default.qubit.' + plugin, wires=2) - state = np.array([0, -1/np.sqrt(2), 1/np.sqrt(2), 0]) - - @qml.qnode(dev) - def circuit(phi): - qml.PauliX(wires=0) - qml.SingleExcitation(phi, wires=[0, 1]) - - return qml.state() - - assert np.allclose(state, circuit(np.pi/2)) - PAULI_ROT_PARAMETRIC_MATRIX_TEST_DATA = [ ( From 31c05b054a0199f4f32994efcf54bcc19353b09a Mon Sep 17 00:00:00 2001 From: ixfoduap Date: Thu, 11 Mar 2021 16:28:41 -0500 Subject: [PATCH 25/50] fixs error --- tests/ops/test_qubit_ops.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/ops/test_qubit_ops.py b/tests/ops/test_qubit_ops.py index 24a48d8fd7e..ce6636a79fa 100644 --- a/tests/ops/test_qubit_ops.py +++ b/tests/ops/test_qubit_ops.py @@ -23,7 +23,6 @@ import pennylane as qml from pennylane.wires import Wires -from pennylane.devices import tf_ops, autograd_ops from gate_data import I, X, Y, Z, H, CNOT, SWAP, CZ, S, T, CSWAP, Toffoli, QFT, \ ControlledPhaseShift, SingleExcitation, SingleExcitationPlus, SingleExcitationMinus From a0bc66a8588f1785de0d51d10218d5dff43c08bc Mon Sep 17 00:00:00 2001 From: ixfoduap Date: Thu, 11 Mar 2021 16:41:29 -0500 Subject: [PATCH 26/50] Adds interface test --- tests/ops/test_qubit_ops.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/ops/test_qubit_ops.py b/tests/ops/test_qubit_ops.py index ce6636a79fa..d065606a62a 100644 --- a/tests/ops/test_qubit_ops.py +++ b/tests/ops/test_qubit_ops.py @@ -933,6 +933,23 @@ def test_single_excitation_minus_generator(self, phi): exp = SingleExcitationMinus(phi) assert np.allclose(res, exp) + @pytest.mark.parametrize("plugin", ['autograd', 'tf', 'jax']) + def test_autograd(self, plugin): + """Tests that gradients and operations are computed correctly using the + autograd interface""" + + dev = qml.device('default.qubit.' + plugin, wires=2) + state = np.array([0, -1 / np.sqrt(2), 1 / np.sqrt(2), 0]) + + @qml.qnode(dev) + def circuit(phi): + qml.PauliX(wires=0) + qml.SingleExcitation(phi, wires=[0, 1]) + + return qml.state() + + assert np.allclose(state, circuit(np.pi / 2)) + PAULI_ROT_PARAMETRIC_MATRIX_TEST_DATA = [ ( From af9f4495fa94aea2282ea69cdbb90029e68cdccb Mon Sep 17 00:00:00 2001 From: ixfoduap Date: Thu, 11 Mar 2021 16:56:37 -0500 Subject: [PATCH 27/50] removes interface tests --- tests/ops/test_qubit_ops.py | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/tests/ops/test_qubit_ops.py b/tests/ops/test_qubit_ops.py index d065606a62a..ce6636a79fa 100644 --- a/tests/ops/test_qubit_ops.py +++ b/tests/ops/test_qubit_ops.py @@ -933,23 +933,6 @@ def test_single_excitation_minus_generator(self, phi): exp = SingleExcitationMinus(phi) assert np.allclose(res, exp) - @pytest.mark.parametrize("plugin", ['autograd', 'tf', 'jax']) - def test_autograd(self, plugin): - """Tests that gradients and operations are computed correctly using the - autograd interface""" - - dev = qml.device('default.qubit.' + plugin, wires=2) - state = np.array([0, -1 / np.sqrt(2), 1 / np.sqrt(2), 0]) - - @qml.qnode(dev) - def circuit(phi): - qml.PauliX(wires=0) - qml.SingleExcitation(phi, wires=[0, 1]) - - return qml.state() - - assert np.allclose(state, circuit(np.pi / 2)) - PAULI_ROT_PARAMETRIC_MATRIX_TEST_DATA = [ ( From f141160c00de9cf1fd6988f9b668adb066947114 Mon Sep 17 00:00:00 2001 From: ixfoduap Date: Mon, 15 Mar 2021 16:45:03 -0400 Subject: [PATCH 28/50] adds new interface tests --- tests/ops/test_qubit_ops.py | 57 +++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/tests/ops/test_qubit_ops.py b/tests/ops/test_qubit_ops.py index ce6636a79fa..47871d4e711 100644 --- a/tests/ops/test_qubit_ops.py +++ b/tests/ops/test_qubit_ops.py @@ -934,6 +934,63 @@ def test_single_excitation_minus_generator(self, phi): assert np.allclose(res, exp) + def test_autograd(self): + """Tests that gradients and operations are computed correctly using the + autograd interface""" + + pytest.importorskip("autograd") + + dev = qml.device('default.qubit.autograd', wires=2) + state = np.array([0, -1 / np.sqrt(2), 1 / np.sqrt(2), 0]) + + @qml.qnode(dev) + def circuit(phi): + qml.PauliX(wires=0) + qml.SingleExcitation(phi, wires=[0, 1]) + + return qml.state() + + assert np.allclose(state, circuit(np.pi / 2)) + + + def test_tf(self): + """Tests that gradients and operations are computed correctly using the + tensorflow interface""" + + pytest.importorskip("tensorflow") + + dev = qml.device('default.qubit.tf', wires=2) + state = np.array([0, -1 / np.sqrt(2), 1 / np.sqrt(2), 0]) + + @qml.qnode(dev) + def circuit(phi): + qml.PauliX(wires=0) + qml.SingleExcitation(phi, wires=[0, 1]) + + return qml.state() + + assert np.allclose(state, circuit(np.pi / 2)) + + + def test_jax(self): + """Tests that gradients and operations are computed correctly using the + jax interface""" + + pytest.importorskip("jax") + + dev = qml.device('default.qubit.jax', wires=2) + state = np.array([0, -1 / np.sqrt(2), 1 / np.sqrt(2), 0]) + + @qml.qnode(dev) + def circuit(phi): + qml.PauliX(wires=0) + qml.SingleExcitation(phi, wires=[0, 1]) + + return qml.state() + + assert np.allclose(state, circuit(np.pi / 2)) + + PAULI_ROT_PARAMETRIC_MATRIX_TEST_DATA = [ ( "XY", From 2b04db3f49978c395ffab196e61b22b36134b6dc Mon Sep 17 00:00:00 2001 From: ixfoduap Date: Wed, 17 Mar 2021 11:27:45 -0400 Subject: [PATCH 29/50] adds interface tests for plus and minus excitations --- tests/ops/test_qubit_ops.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/tests/ops/test_qubit_ops.py b/tests/ops/test_qubit_ops.py index 47871d4e711..d069f7d1750 100644 --- a/tests/ops/test_qubit_ops.py +++ b/tests/ops/test_qubit_ops.py @@ -933,8 +933,9 @@ def test_single_excitation_minus_generator(self, phi): exp = SingleExcitationMinus(phi) assert np.allclose(res, exp) - - def test_autograd(self): + @pytest.mark.parametrize("excitation", [qml.SingleExcitation, qml.SingleExcitationPlus, + qml.SingleExcitationMinus]) + def test_autograd(self, excitation): """Tests that gradients and operations are computed correctly using the autograd interface""" @@ -946,14 +947,17 @@ def test_autograd(self): @qml.qnode(dev) def circuit(phi): qml.PauliX(wires=0) - qml.SingleExcitation(phi, wires=[0, 1]) + excitation(phi, wires=[0, 1]) return qml.state() assert np.allclose(state, circuit(np.pi / 2)) + # Add qml.grad() - def test_tf(self): + @pytest.mark.parametrize("excitation", [qml.SingleExcitation, qml.SingleExcitationPlus, + qml.SingleExcitationMinus]) + def test_tf(self, excitation): """Tests that gradients and operations are computed correctly using the tensorflow interface""" @@ -971,8 +975,9 @@ def circuit(phi): assert np.allclose(state, circuit(np.pi / 2)) - - def test_jax(self): + @pytest.mark.parametrize("excitation", [qml.SingleExcitation, qml.SingleExcitationPlus, + qml.SingleExcitationMinus]) + def test_jax(self, excitation): """Tests that gradients and operations are computed correctly using the jax interface""" From c685a6ce814dcb3ed5daa37752f836edfbc35c16 Mon Sep 17 00:00:00 2001 From: ixfoduap Date: Wed, 17 Mar 2021 14:50:38 -0400 Subject: [PATCH 30/50] adds interface gradient tests --- tests/ops/test_qubit_ops.py | 46 ++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/tests/ops/test_qubit_ops.py b/tests/ops/test_qubit_ops.py index d069f7d1750..591a254bc59 100644 --- a/tests/ops/test_qubit_ops.py +++ b/tests/ops/test_qubit_ops.py @@ -936,7 +936,7 @@ def test_single_excitation_minus_generator(self, phi): @pytest.mark.parametrize("excitation", [qml.SingleExcitation, qml.SingleExcitationPlus, qml.SingleExcitationMinus]) def test_autograd(self, excitation): - """Tests that gradients and operations are computed correctly using the + """Tests that operations are computed correctly using the autograd interface""" pytest.importorskip("autograd") @@ -953,47 +953,65 @@ def circuit(phi): assert np.allclose(state, circuit(np.pi / 2)) - # Add qml.grad() + @pytest.mark.parametrize(("excitation", "phi"), [(qml.SingleExcitation, -0.1), + (qml.SingleExcitationPlus, 0.2), + (qml.SingleExcitationMinus, np.pi/4)]) + def test_autograd_grad(self, excitation, phi): + """Tests that gradients are computed correctly using the + autograd interface""" - @pytest.mark.parametrize("excitation", [qml.SingleExcitation, qml.SingleExcitationPlus, - qml.SingleExcitationMinus]) - def test_tf(self, excitation): + pytest.importorskip("autograd") + + dev = qml.device('default.qubit.autograd', wires=2) + + @qml.qnode(dev) + def circuit(phi): + qml.PauliX(wires=0) + excitation(phi, wires=[0, 1]) + + return qml.expval(qml.PauliZ(0)) + + assert np.allclose(qml.grad(circuit)(phi), np.sin(phi)) # gradient = sin(phi) + + @pytest.mark.parametrize(("excitation", "phi"), [(qml.SingleExcitation, -0.1), + (qml.SingleExcitationPlus, 0.2), + (qml.SingleExcitationMinus, np.pi / 4)]) + def test_tf(self, excitation, phi): """Tests that gradients and operations are computed correctly using the tensorflow interface""" pytest.importorskip("tensorflow") dev = qml.device('default.qubit.tf', wires=2) - state = np.array([0, -1 / np.sqrt(2), 1 / np.sqrt(2), 0]) @qml.qnode(dev) def circuit(phi): qml.PauliX(wires=0) qml.SingleExcitation(phi, wires=[0, 1]) - return qml.state() + return qml.expval(qml.PauliZ(0)) - assert np.allclose(state, circuit(np.pi / 2)) + assert np.allclose(qml.grad(circuit)(phi), np.sin(phi)) # gradient = sin(phi) - @pytest.mark.parametrize("excitation", [qml.SingleExcitation, qml.SingleExcitationPlus, - qml.SingleExcitationMinus]) - def test_jax(self, excitation): + @pytest.mark.parametrize(("excitation", "phi"), [(qml.SingleExcitation, -0.1), + (qml.SingleExcitationPlus, 0.2), + (qml.SingleExcitationMinus, np.pi / 4)]) + def test_jax(self, excitation, phi): """Tests that gradients and operations are computed correctly using the jax interface""" pytest.importorskip("jax") dev = qml.device('default.qubit.jax', wires=2) - state = np.array([0, -1 / np.sqrt(2), 1 / np.sqrt(2), 0]) @qml.qnode(dev) def circuit(phi): qml.PauliX(wires=0) qml.SingleExcitation(phi, wires=[0, 1]) - return qml.state() + return qml.expval(qml.PauliZ(0)) - assert np.allclose(state, circuit(np.pi / 2)) + assert np.allclose(qml.grad(circuit)(phi), np.sin(phi)) # gradient = sin(phi) PAULI_ROT_PARAMETRIC_MATRIX_TEST_DATA = [ From a0b83289e5a67bb353c4b5ceac14d2c2fb16d8a1 Mon Sep 17 00:00:00 2001 From: ixfoduap <40441298+ixfoduap@users.noreply.github.com> Date: Thu, 18 Mar 2021 09:31:19 -0400 Subject: [PATCH 31/50] Update tests/ops/test_qubit_ops.py Co-authored-by: Josh Izaac --- tests/ops/test_qubit_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ops/test_qubit_ops.py b/tests/ops/test_qubit_ops.py index 591a254bc59..894f49b5920 100644 --- a/tests/ops/test_qubit_ops.py +++ b/tests/ops/test_qubit_ops.py @@ -1007,7 +1007,7 @@ def test_jax(self, excitation, phi): @qml.qnode(dev) def circuit(phi): qml.PauliX(wires=0) - qml.SingleExcitation(phi, wires=[0, 1]) + excitation(phi, wires=[0, 1]) return qml.expval(qml.PauliZ(0)) From be1605677298468ac7d1024641737e18e409219c Mon Sep 17 00:00:00 2001 From: ixfoduap <40441298+ixfoduap@users.noreply.github.com> Date: Thu, 18 Mar 2021 09:31:25 -0400 Subject: [PATCH 32/50] Update tests/ops/test_qubit_ops.py Co-authored-by: Josh Izaac --- tests/ops/test_qubit_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ops/test_qubit_ops.py b/tests/ops/test_qubit_ops.py index 894f49b5920..5a2cda4b018 100644 --- a/tests/ops/test_qubit_ops.py +++ b/tests/ops/test_qubit_ops.py @@ -987,7 +987,7 @@ def test_tf(self, excitation, phi): @qml.qnode(dev) def circuit(phi): qml.PauliX(wires=0) - qml.SingleExcitation(phi, wires=[0, 1]) + excitation(phi, wires=[0, 1]) return qml.expval(qml.PauliZ(0)) From b63561c2db8f3954c47b0cf627fd235f8db727df Mon Sep 17 00:00:00 2001 From: ixfoduap <40441298+ixfoduap@users.noreply.github.com> Date: Thu, 18 Mar 2021 15:08:51 -0400 Subject: [PATCH 33/50] Apply suggestions from code review Co-authored-by: Josh Izaac Co-authored-by: antalszava --- .github/CHANGELOG.md | 1 - pennylane/devices/autograd_ops.py | 3 +-- pennylane/ops/qubit.py | 6 ++--- tests/ops/test_qubit_ops.py | 38 +++++++++++++++++++------------ 4 files changed, 28 insertions(+), 20 deletions(-) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 897f07cb4e8..3ac866a5842 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -394,7 +394,6 @@ This release contains contributions from (in alphabetical order): - Juan Miguel Arrazola, Thomas Bromley, Olivia Di Matteo, Kyle Godbey, Diego Guala, Josh Izaac, Daniel Polatajko, Chase Roberts, Sankalp Sanand, Pritish Sehzpaul, Maria Schuld, Antal Száva. diff --git a/pennylane/devices/autograd_ops.py b/pennylane/devices/autograd_ops.py index e3163de245b..6722054490c 100644 --- a/pennylane/devices/autograd_ops.py +++ b/pennylane/devices/autograd_ops.py @@ -183,8 +183,7 @@ def MultiRZ(theta, n): def SingleExcitation(phi): - r""" - Single excitation rotation. + r"""Single excitation rotation. Args: phi (float): rotation angle diff --git a/pennylane/ops/qubit.py b/pennylane/ops/qubit.py index af75120e958..5252d8f70e9 100644 --- a/pennylane/ops/qubit.py +++ b/pennylane/ops/qubit.py @@ -1679,7 +1679,7 @@ class SingleExcitation(Operation): This operation performs a rotation in the two-dimensional subspace :math:`\{|01\rangle, |10\rangle\}`. The name originates from a fermionic interpretation, where the transformation - from :math:`|10\rangle` to `:math:`|01\rangle` is interpreted as "exciting" a particle from + from :math:`|10\rangle` to :math:`|01\rangle` is interpreted as "exciting" a particle from the first qubit to the second. **Details:** @@ -1687,7 +1687,7 @@ class SingleExcitation(Operation): * Number of wires: 2 * Number of parameters: 1 * Gradient recipe: Obtained from its decomposition in terms of the - :class:`~.SingleExcitationPlus` and `:class:`~.SingleExcitationMinus` operations + :class:`~.SingleExcitationPlus` and :class:`~.SingleExcitationMinus` operations Args: phi (float): rotation angle :math:`\phi` @@ -1697,7 +1697,7 @@ class SingleExcitation(Operation): **Example** The following circuit performs the transformation :math:`|10\rangle\rightarrow \cos( - \phi/2)|10\rangle -\sin(\phi/2)|01\rangle)`: + \phi/2)|10\rangle -\sin(\phi/2)|01\rangle`: .. code-block:: diff --git a/tests/ops/test_qubit_ops.py b/tests/ops/test_qubit_ops.py index 5a2cda4b018..603f19647de 100644 --- a/tests/ops/test_qubit_ops.py +++ b/tests/ops/test_qubit_ops.py @@ -918,7 +918,7 @@ def test_single_excitation_plus_generator(self, phi): @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi/4]) def test_single_excitation_minus_matrix(self, phi): - """Tests that the SingleExcitationPlus operation calculates the correct matrix""" + """Tests that the SingleExcitationMinus operation calculates the correct matrix""" op = qml.SingleExcitationMinus(phi, wires=[0, 1]) res = op.matrix exp = SingleExcitationMinus(phi) @@ -926,7 +926,7 @@ def test_single_excitation_minus_matrix(self, phi): @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi/4]) def test_single_excitation_minus_generator(self, phi): - """Tests that the SingleExcitationPlus operation calculates the correct generator""" + """Tests that the SingleExcitationMinus operation calculates the correct generator""" op = qml.SingleExcitationMinus(phi, wires=[0, 1]) g, a = op.generator res = expm(1j * a * g * phi) @@ -973,45 +973,55 @@ def circuit(phi): assert np.allclose(qml.grad(circuit)(phi), np.sin(phi)) # gradient = sin(phi) + @pytest.mark.parametrize("diff_method", ["parameter-shift", "backprop"]) @pytest.mark.parametrize(("excitation", "phi"), [(qml.SingleExcitation, -0.1), (qml.SingleExcitationPlus, 0.2), (qml.SingleExcitationMinus, np.pi / 4)]) - def test_tf(self, excitation, phi): + def test_tf(self, excitation, phi, diff_method): """Tests that gradients and operations are computed correctly using the tensorflow interface""" - pytest.importorskip("tensorflow") + tf = pytest.importorskip("tensorflow") - dev = qml.device('default.qubit.tf', wires=2) + dev = qml.device('default.qubit', wires=2) - @qml.qnode(dev) + @qml.qnode(dev, interface="tf", diff_method=diff_method) def circuit(phi): qml.PauliX(wires=0) excitation(phi, wires=[0, 1]) - return qml.expval(qml.PauliZ(0)) + + phi_t = tf.Variable(phi, dtype=tf.float64) + + with tf.GradientTape() as tape: + res = circuit(phi_t) - assert np.allclose(qml.grad(circuit)(phi), np.sin(phi)) # gradient = sin(phi) + grad = tape.gradient(res, phi_t) + assert np.allclose(grad, np.sin(phi)) + @pytest.mark.parametrize("diff_method", ["parameter-shift", "backprop"]) @pytest.mark.parametrize(("excitation", "phi"), [(qml.SingleExcitation, -0.1), (qml.SingleExcitationPlus, 0.2), (qml.SingleExcitationMinus, np.pi / 4)]) - def test_jax(self, excitation, phi): + def test_jax(self, excitation, phi, diff_method): """Tests that gradients and operations are computed correctly using the jax interface""" + + if diff_method="parameter-shift": + pytest.skip("JAX support for the parameter-shift method is still TBD") - pytest.importorskip("jax") + jax = pytest.importorskip("jax") + from jax import numpy as jnp - dev = qml.device('default.qubit.jax', wires=2) + dev = qml.device('default.qubit', wires=2) - @qml.qnode(dev) + @qml.qnode(dev, interface="jax", diff_method=diff_method) def circuit(phi): qml.PauliX(wires=0) excitation(phi, wires=[0, 1]) - return qml.expval(qml.PauliZ(0)) - assert np.allclose(qml.grad(circuit)(phi), np.sin(phi)) # gradient = sin(phi) + assert np.allclose(jax.grad(circuit)(phi), np.sin(phi)) PAULI_ROT_PARAMETRIC_MATRIX_TEST_DATA = [ From 0875d71622e853aad431620965eda9cc27600d60 Mon Sep 17 00:00:00 2001 From: ixfoduap <40441298+ixfoduap@users.noreply.github.com> Date: Thu, 18 Mar 2021 15:09:48 -0400 Subject: [PATCH 34/50] Apply suggestions from code review --- .github/CHANGELOG.md | 13 ++++--------- pennylane/ops/qubit.py | 4 ++-- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 3ac866a5842..99caaa46805 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -5,9 +5,9 @@ - Added the `SingleExcitation` two-qubit operation, which is useful for quantum chemistry applications. [(#1121)](https://github.com/PennyLaneAI/pennylane/pull/1121) - It can be used to perform an :math:`SO(2)` rotation in the subspace - spanned by the states :math:`|01\rangle, |10\rangle`. For example, the following circuit - performs the transformation :math:`|10\rangle\rightarrow \cos(\phi/2)|10\rangle -\sin(\phi/2)|01\rangle)`: + It can be used to perform an SO(2) rotation in the subspace + spanned by the states |01>, |10>. For example, the following circuit + performs the transformation |10> --> cos(phi/2)|10> -sin(phi/2)|01>`: ```python dev = qml.device('default.qubit', wires=2) @@ -18,12 +18,7 @@ qml.SingleExcitation(phi, wires=[0, 1]) ``` - The `SingleExcitation` operation supports analytical gradients via its decomposition - in terms of the `SingleExcitationPlus` and `SingleExcitationMinus` operations, whose - gradients are given by the standard parameter-shift rule. These are also now supported. - Instead of acting as the identity on the subspace :math:`|00\rangle, |11\rangle`, the - `SingleExcitationPlus` and `SingleExcitationMinus` operations respectively apply a - positive/negative phase-shift on this subspace. + The `SingleExcitation` operation supports analytical gradients on hardware using only four expectation value calculations, following results from [(Kottmann et al.)](https://arxiv.org/abs/2011.05938) * Added the function ``finite_diff()`` to compute finite-difference approximations to the gradient and the second-order derivatives of diff --git a/pennylane/ops/qubit.py b/pennylane/ops/qubit.py index 5252d8f70e9..0fa712ae0ec 100644 --- a/pennylane/ops/qubit.py +++ b/pennylane/ops/qubit.py @@ -1745,7 +1745,7 @@ class SingleExcitationMinus(Operation): * Number of wires: 2 * Number of parameters: 1 - * Gradient recipe: :math:`\frac{d}{d\phi}f(R_y(\phi)) = U_-\frac{1}{2}\left[f(U_+(\phi+\pi/2)) - f(U_-(\phi-\pi/2))\right]` + * Gradient recipe: :math:`\frac{d}{d\phi}f(R_y(\phi)) = \frac{1}{2}\left[f(U_+(\phi+\pi/2)) - f(U_-(\phi-\pi/2))\right]` where :math:`f` is an expectation value depending on :math:`U_-(\phi)`. Args: @@ -1784,7 +1784,7 @@ class SingleExcitationPlus(Operation): * Number of wires: 2 * Number of parameters: 1 - * Gradient recipe: :math:`\frac{d}{d\phi}f(R_y(\phi)) = U_+\frac{1}{2}\left[f(U_+(\phi+\pi/2)) - f(U_+(\phi-\pi/2))\right]` + * Gradient recipe: :math:`\frac{d}{d\phi}f(R_y(\phi)) = \frac{1}{2}\left[f(U_+(\phi+\pi/2)) - f(U_+(\phi-\pi/2))\right]` where :math:`f` is an expectation value depending on :math:`U_+(\phi)`. Args: From efb0fc4d5cc5bee0db199d24318f163a503fd24b Mon Sep 17 00:00:00 2001 From: ixfoduap Date: Thu, 18 Mar 2021 15:53:32 -0400 Subject: [PATCH 35/50] adds suggestions from code review --- pennylane/devices/tests/test_gates.py | 3 +++ pennylane/ops/qubit.py | 10 ++++++---- tests/ops/test_qubit_ops.py | 15 ++------------- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/pennylane/devices/tests/test_gates.py b/pennylane/devices/tests/test_gates.py index e297ff8037a..1721b1f0e34 100755 --- a/pennylane/devices/tests/test_gates.py +++ b/pennylane/devices/tests/test_gates.py @@ -68,6 +68,9 @@ "SX": qml.SX(wires=[0]), "Toffoli": qml.Toffoli(wires=[0, 1, 2]), "QFT": qml.QFT(wires=[0, 1, 2]), + "SingleExcitation": qml.SingleExcitation(0, wires=[0, 1]), + "SingleExcitationPlus": qml.SingleExcitationPlus(0, wires=[0, 1]), + "SingleExcitationMinus": qml.SingleExcitationMinus(0, wires=[0, 1]) } all_ops = ops.keys() diff --git a/pennylane/ops/qubit.py b/pennylane/ops/qubit.py index 0fa712ae0ec..3844f84d943 100644 --- a/pennylane/ops/qubit.py +++ b/pennylane/ops/qubit.py @@ -1745,8 +1745,9 @@ class SingleExcitationMinus(Operation): * Number of wires: 2 * Number of parameters: 1 - * Gradient recipe: :math:`\frac{d}{d\phi}f(R_y(\phi)) = \frac{1}{2}\left[f(U_+(\phi+\pi/2)) - f(U_-(\phi-\pi/2))\right]` - where :math:`f` is an expectation value depending on :math:`U_-(\phi)`. + * Gradient recipe: :math:`\frac{d}{d\phi}f(U_-(\phi)) = \frac{1}{2}\left[f(U_-(\phi+\pi/2)) - + f(U_-(\phi-\pi/2))\right]` where :math:`f` is an expectation value depending on + :math:`U_-(\phi)`. Args: phi (float): rotation angle :math:`\phi` @@ -1784,8 +1785,9 @@ class SingleExcitationPlus(Operation): * Number of wires: 2 * Number of parameters: 1 - * Gradient recipe: :math:`\frac{d}{d\phi}f(R_y(\phi)) = \frac{1}{2}\left[f(U_+(\phi+\pi/2)) - f(U_+(\phi-\pi/2))\right]` - where :math:`f` is an expectation value depending on :math:`U_+(\phi)`. + * Gradient recipe: :math:`\frac{d}{d\phi}f(U_+(\phi)) = \frac{1}{2}\left[f(U_+(\phi+\pi/2)) - + f(U_+(\phi-\pi/2))\right]` where :math:`f` is an expectation value depending + on :math:`U_+(\phi)`. Args: phi (float): rotation angle :math:`\phi` diff --git a/tests/ops/test_qubit_ops.py b/tests/ops/test_qubit_ops.py index 603f19647de..2b2c7d3c66d 100644 --- a/tests/ops/test_qubit_ops.py +++ b/tests/ops/test_qubit_ops.py @@ -882,12 +882,9 @@ def test_single_excitation_decomp(self, phi): """Tests that the SingleExcitation operation calculates the correct decomposition""" op = qml.SingleExcitation(phi, wires=[0, 1]) decomp = op.decomposition(phi, wires=[0, 1]) - mats = [m.matrix for m in decomp] - decomposed_matrix = mats[0] @ mats[1] exp = SingleExcitation(phi) - assert np.allclose(decomposed_matrix, exp) @pytest.mark.parametrize("phi", [-0.1, 0.2, np.pi/4]) @@ -940,7 +937,6 @@ def test_autograd(self, excitation): autograd interface""" pytest.importorskip("autograd") - dev = qml.device('default.qubit.autograd', wires=2) state = np.array([0, -1 / np.sqrt(2), 1 / np.sqrt(2), 0]) @@ -948,9 +944,7 @@ def test_autograd(self, excitation): def circuit(phi): qml.PauliX(wires=0) excitation(phi, wires=[0, 1]) - return qml.state() - assert np.allclose(state, circuit(np.pi / 2)) @pytest.mark.parametrize(("excitation", "phi"), [(qml.SingleExcitation, -0.1), @@ -961,17 +955,14 @@ def test_autograd_grad(self, excitation, phi): autograd interface""" pytest.importorskip("autograd") - dev = qml.device('default.qubit.autograd', wires=2) @qml.qnode(dev) def circuit(phi): qml.PauliX(wires=0) excitation(phi, wires=[0, 1]) - return qml.expval(qml.PauliZ(0)) - - assert np.allclose(qml.grad(circuit)(phi), np.sin(phi)) # gradient = sin(phi) + assert np.allclose(qml.grad(circuit)(phi), np.sin(phi)) @pytest.mark.parametrize("diff_method", ["parameter-shift", "backprop"]) @pytest.mark.parametrize(("excitation", "phi"), [(qml.SingleExcitation, -0.1), @@ -982,7 +973,6 @@ def test_tf(self, excitation, phi, diff_method): tensorflow interface""" tf = pytest.importorskip("tensorflow") - dev = qml.device('default.qubit', wires=2) @qml.qnode(dev, interface="tf", diff_method=diff_method) @@ -992,7 +982,6 @@ def circuit(phi): return qml.expval(qml.PauliZ(0)) phi_t = tf.Variable(phi, dtype=tf.float64) - with tf.GradientTape() as tape: res = circuit(phi_t) @@ -1007,7 +996,7 @@ def test_jax(self, excitation, phi, diff_method): """Tests that gradients and operations are computed correctly using the jax interface""" - if diff_method="parameter-shift": + if diff_method=="parameter-shift": pytest.skip("JAX support for the parameter-shift method is still TBD") jax = pytest.importorskip("jax") From 63a57d38ec7d15a9365829294a98ed877d5ba293 Mon Sep 17 00:00:00 2001 From: ixfoduap Date: Thu, 18 Mar 2021 16:34:48 -0400 Subject: [PATCH 36/50] black --- pennylane/devices/tests/test_gates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/devices/tests/test_gates.py b/pennylane/devices/tests/test_gates.py index 1721b1f0e34..6c6aa6a046b 100755 --- a/pennylane/devices/tests/test_gates.py +++ b/pennylane/devices/tests/test_gates.py @@ -70,7 +70,7 @@ "QFT": qml.QFT(wires=[0, 1, 2]), "SingleExcitation": qml.SingleExcitation(0, wires=[0, 1]), "SingleExcitationPlus": qml.SingleExcitationPlus(0, wires=[0, 1]), - "SingleExcitationMinus": qml.SingleExcitationMinus(0, wires=[0, 1]) + "SingleExcitationMinus": qml.SingleExcitationMinus(0, wires=[0, 1]), } all_ops = ops.keys() From ea45124625ed0e79dd984e5f91ce0a0acf949ced Mon Sep 17 00:00:00 2001 From: ixfoduap Date: Thu, 18 Mar 2021 17:04:14 -0400 Subject: [PATCH 37/50] fixes indentation --- pennylane/ops/qubit.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pennylane/ops/qubit.py b/pennylane/ops/qubit.py index 3844f84d943..76ba540e910 100644 --- a/pennylane/ops/qubit.py +++ b/pennylane/ops/qubit.py @@ -1746,8 +1746,7 @@ class SingleExcitationMinus(Operation): * Number of wires: 2 * Number of parameters: 1 * Gradient recipe: :math:`\frac{d}{d\phi}f(U_-(\phi)) = \frac{1}{2}\left[f(U_-(\phi+\pi/2)) - - f(U_-(\phi-\pi/2))\right]` where :math:`f` is an expectation value depending on - :math:`U_-(\phi)`. + f(U_-(\phi-\pi/2))\right]` where :math:`f` is an expectation value depending on :math:`U_-(\phi)`. Args: phi (float): rotation angle :math:`\phi` @@ -1786,8 +1785,7 @@ class SingleExcitationPlus(Operation): * Number of wires: 2 * Number of parameters: 1 * Gradient recipe: :math:`\frac{d}{d\phi}f(U_+(\phi)) = \frac{1}{2}\left[f(U_+(\phi+\pi/2)) - - f(U_+(\phi-\pi/2))\right]` where :math:`f` is an expectation value depending - on :math:`U_+(\phi)`. + f(U_+(\phi-\pi/2))\right]` where :math:`f` is an expectation value depending on :math:`U_+(\phi)`. Args: phi (float): rotation angle :math:`\phi` From 5e7eae1072eab95da94d49148bb721c9f65366b8 Mon Sep 17 00:00:00 2001 From: ixfoduap <40441298+ixfoduap@users.noreply.github.com> Date: Thu, 18 Mar 2021 17:05:21 -0400 Subject: [PATCH 38/50] Update .github/CHANGELOG.md --- .github/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 99caaa46805..f230fdb5685 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -7,7 +7,7 @@ It can be used to perform an SO(2) rotation in the subspace spanned by the states |01>, |10>. For example, the following circuit - performs the transformation |10> --> cos(phi/2)|10> -sin(phi/2)|01>`: + performs the transformation |10> --> cos(phi/2)|10> - sin(phi/2)|01>`: ```python dev = qml.device('default.qubit', wires=2) From 346edfa1302ee081485e346d499f999ebed95ede Mon Sep 17 00:00:00 2001 From: ixfoduap Date: Thu, 18 Mar 2021 17:14:10 -0400 Subject: [PATCH 39/50] indentation --- pennylane/ops/qubit.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pennylane/ops/qubit.py b/pennylane/ops/qubit.py index 76ba540e910..a74d2748bea 100644 --- a/pennylane/ops/qubit.py +++ b/pennylane/ops/qubit.py @@ -1745,8 +1745,8 @@ class SingleExcitationMinus(Operation): * Number of wires: 2 * Number of parameters: 1 - * Gradient recipe: :math:`\frac{d}{d\phi}f(U_-(\phi)) = \frac{1}{2}\left[f(U_-(\phi+\pi/2)) - - f(U_-(\phi-\pi/2))\right]` where :math:`f` is an expectation value depending on :math:`U_-(\phi)`. + * Gradient recipe: :math:`\frac{d}{d\phi}f(U_-(\phi)) = \frac{1}{2}\left[f(U_-(\phi+\pi/2)) - f(U_-(\phi-\pi/2))\right]` + where :math:`f` is an expectation value depending on :math:`U_-(\phi)`. Args: phi (float): rotation angle :math:`\phi` @@ -1784,8 +1784,8 @@ class SingleExcitationPlus(Operation): * Number of wires: 2 * Number of parameters: 1 - * Gradient recipe: :math:`\frac{d}{d\phi}f(U_+(\phi)) = \frac{1}{2}\left[f(U_+(\phi+\pi/2)) - - f(U_+(\phi-\pi/2))\right]` where :math:`f` is an expectation value depending on :math:`U_+(\phi)`. + * Gradient recipe: :math:`\frac{d}{d\phi}f(U_+(\phi)) = \frac{1}{2}\left[f(U_+(\phi+\pi/2)) - f(U_+(\phi-\pi/2))\right]` + where :math:`f` is an expectation value depending on :math:`U_+(\phi)`. Args: phi (float): rotation angle :math:`\phi` From 95d9abcc0f21910cc25513d03b1926f33d6646cb Mon Sep 17 00:00:00 2001 From: ixfoduap <40441298+ixfoduap@users.noreply.github.com> Date: Thu, 18 Mar 2021 17:26:22 -0400 Subject: [PATCH 40/50] Update .github/CHANGELOG.md --- .github/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index f230fdb5685..273cfa64dfb 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -18,7 +18,7 @@ qml.SingleExcitation(phi, wires=[0, 1]) ``` - The `SingleExcitation` operation supports analytical gradients on hardware using only four expectation value calculations, following results from [(Kottmann et al.)](https://arxiv.org/abs/2011.05938) + The `SingleExcitation` operation supports analytical gradients on hardware using only four expectation value calculations, following results from [Kottmann et al.](https://arxiv.org/abs/2011.05938) * Added the function ``finite_diff()`` to compute finite-difference approximations to the gradient and the second-order derivatives of From 0c059eabcc1cac54793cefe35191d01ab42d5c97 Mon Sep 17 00:00:00 2001 From: ixfoduap Date: Thu, 18 Mar 2021 17:39:51 -0400 Subject: [PATCH 41/50] fixes changelog --- .github/CHANGELOG.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 273cfa64dfb..2dd1dd0a65c 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -2,12 +2,12 @@

New features since last release

-- Added the `SingleExcitation` two-qubit operation, which is useful for quantum +* Added the `SingleExcitation` two-qubit operation, which is useful for quantum chemistry applications. [(#1121)](https://github.com/PennyLaneAI/pennylane/pull/1121) It can be used to perform an SO(2) rotation in the subspace spanned by the states |01>, |10>. For example, the following circuit - performs the transformation |10> --> cos(phi/2)|10> - sin(phi/2)|01>`: + performs the transformation |10> --> cos(phi/2)|10> - sin(phi/2)|01>: ```python dev = qml.device('default.qubit', wires=2) @@ -18,7 +18,9 @@ qml.SingleExcitation(phi, wires=[0, 1]) ``` - The `SingleExcitation` operation supports analytical gradients on hardware using only four expectation value calculations, following results from [Kottmann et al.](https://arxiv.org/abs/2011.05938) + The `SingleExcitation` operation supports analytical gradients on hardware + using only four expectation value calculations, following results from + [Kottmann et al.](https://arxiv.org/abs/2011.05938) * Added the function ``finite_diff()`` to compute finite-difference approximations to the gradient and the second-order derivatives of From d85a148cbb7720369eec787c9bd2f97588bb8119 Mon Sep 17 00:00:00 2001 From: ixfoduap Date: Thu, 18 Mar 2021 18:18:45 -0400 Subject: [PATCH 42/50] this should fix it --- .github/CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 2dd1dd0a65c..f2129559c09 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -6,8 +6,9 @@ chemistry applications. [(#1121)](https://github.com/PennyLaneAI/pennylane/pull/1121) It can be used to perform an SO(2) rotation in the subspace - spanned by the states |01>, |10>. For example, the following circuit - performs the transformation |10> --> cos(phi/2)|10> - sin(phi/2)|01>: + spanned by the states :math:`|01\rangle` and :math:`|10\rangle`. + For example, the following circuit performs the transformation + :math:`|10\rangle \rightarrow \cos(\phi/2)|10\rangle - \sin(\phi/2)|01\rangle`: ```python dev = qml.device('default.qubit', wires=2) From c297f75a0045c924a90ce2a5cf6653d52cd06378 Mon Sep 17 00:00:00 2001 From: ixfoduap <40441298+ixfoduap@users.noreply.github.com> Date: Fri, 19 Mar 2021 09:31:48 -0400 Subject: [PATCH 43/50] Apply suggestions from code review --- pennylane/devices/autograd_ops.py | 3 ++- pennylane/devices/jax_ops.py | 3 ++- pennylane/devices/tf_ops.py | 3 ++- pennylane/ops/qubit.py | 6 +++--- tests/gate_data.py | 2 +- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/pennylane/devices/autograd_ops.py b/pennylane/devices/autograd_ops.py index 6722054490c..459ded75694 100644 --- a/pennylane/devices/autograd_ops.py +++ b/pennylane/devices/autograd_ops.py @@ -189,7 +189,8 @@ def SingleExcitation(phi): phi (float): rotation angle Returns: - array[complex]: Single excitation rotation matrix + array[float]: Single excitation rotation matrix + """ diff --git a/pennylane/devices/jax_ops.py b/pennylane/devices/jax_ops.py index 3951b4ea06c..3d7e8367c90 100644 --- a/pennylane/devices/jax_ops.py +++ b/pennylane/devices/jax_ops.py @@ -190,7 +190,8 @@ def SingleExcitation(phi): phi (float): rotation angle Returns: - jnp.Tensor[complex]: Single excitation rotation matrix + jnp.Tensor[float]: Single excitation rotation matrix + """ c = jnp.cos(phi / 2) diff --git a/pennylane/devices/tf_ops.py b/pennylane/devices/tf_ops.py index e954cfe41e7..97177dd1f3c 100644 --- a/pennylane/devices/tf_ops.py +++ b/pennylane/devices/tf_ops.py @@ -201,7 +201,8 @@ def SingleExcitation(phi): phi (float): rotation angle Returns: - tf.Tensor[complex]: Single excitation rotation matrix + tf.Tensor[float]: Single excitation rotation matrix + """ phi = tf.cast(phi, dtype=C_DTYPE) diff --git a/pennylane/ops/qubit.py b/pennylane/ops/qubit.py index a74d2748bea..90ddeca7462 100644 --- a/pennylane/ops/qubit.py +++ b/pennylane/ops/qubit.py @@ -1678,9 +1678,9 @@ class SingleExcitation(Operation): \end{bmatrix}. This operation performs a rotation in the two-dimensional subspace :math:`\{|01\rangle, - |10\rangle\}`. The name originates from a fermionic interpretation, where the transformation - from :math:`|10\rangle` to :math:`|01\rangle` is interpreted as "exciting" a particle from - the first qubit to the second. + |10\rangle\}`. The name originates from the occupation-number representation of + fermionic wavefunctions, where the transformation from :math:`|10\rangle` to :math:`|01\rangle` + is interpreted as "exciting" a particle from the first qubit to the second. **Details:** diff --git a/tests/gate_data.py b/tests/gate_data.py index 96331594845..44836637da6 100644 --- a/tests/gate_data.py +++ b/tests/gate_data.py @@ -242,7 +242,7 @@ def SingleExcitation(phi): phi (float): rotation angle Returns: - array: the two-qubit Givens rotation describing the single excitation + array: the two-qubit Givens rotation describing the single excitation operation """ return np.array([[1, 0, 0, 0], [0, np.cos(phi/2), -np.sin(phi/2), 0], From 434a9ba55f5843311306d5f7a5e8803b91b390a6 Mon Sep 17 00:00:00 2001 From: ixfoduap Date: Fri, 19 Mar 2021 09:41:58 -0400 Subject: [PATCH 44/50] fix indentation --- pennylane/ops/qubit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/ops/qubit.py b/pennylane/ops/qubit.py index 90ddeca7462..27a81957102 100644 --- a/pennylane/ops/qubit.py +++ b/pennylane/ops/qubit.py @@ -1680,7 +1680,7 @@ class SingleExcitation(Operation): This operation performs a rotation in the two-dimensional subspace :math:`\{|01\rangle, |10\rangle\}`. The name originates from the occupation-number representation of fermionic wavefunctions, where the transformation from :math:`|10\rangle` to :math:`|01\rangle` - is interpreted as "exciting" a particle from the first qubit to the second. + is interpreted as "exciting" a particle from the first qubit to the second. **Details:** From 2476d1fd74b5b88c6a2f044e2fffdf6348a880e4 Mon Sep 17 00:00:00 2001 From: ixfoduap Date: Fri, 19 Mar 2021 14:24:46 -0400 Subject: [PATCH 45/50] improvements to docstrings --- doc/conf.py | 2 +- pennylane/devices/autograd_ops.py | 13 ++----------- pennylane/devices/jax_ops.py | 13 +++---------- pennylane/devices/tf_ops.py | 13 +++---------- 4 files changed, 9 insertions(+), 32 deletions(-) diff --git a/doc/conf.py b/doc/conf.py index f08189810df..3e7e43a8509 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -46,7 +46,7 @@ "sphinx.ext.intersphinx", "sphinx_automodapi.automodapi", 'sphinx_copybutton', - "m2r" + # "m2r" ] source_suffix = ['.rst', '.md'] diff --git a/pennylane/devices/autograd_ops.py b/pennylane/devices/autograd_ops.py index 459ded75694..905df14b769 100644 --- a/pennylane/devices/autograd_ops.py +++ b/pennylane/devices/autograd_ops.py @@ -190,9 +190,6 @@ def SingleExcitation(phi): Returns: array[float]: Single excitation rotation matrix - - - """ c = np.cos(phi / 2) s = np.sin(phi / 2) @@ -200,16 +197,13 @@ def SingleExcitation(phi): def SingleExcitationPlus(phi): - r""" - Single excitation rotation with positive phase-shift outside the rotation subspace. + r"""Single excitation rotation with positive phase-shift outside the rotation subspace. Args: phi (float): rotation angle Returns: array[complex]: Single excitation rotation matrix with positive phase-shift - - """ c = np.cos(phi / 2) s = np.sin(phi / 2) @@ -218,16 +212,13 @@ def SingleExcitationPlus(phi): def SingleExcitationMinus(phi): - r""" - Single excitation rotation with negative phase-shift outside the rotation subspace. + r"""Single excitation rotation with negative phase-shift outside the rotation subspace. Args: phi (float): rotation angle Returns: array[complex]: Single excitation rotation matrix with negative phase-shift - - """ c = np.cos(phi / 2) s = np.sin(phi / 2) diff --git a/pennylane/devices/jax_ops.py b/pennylane/devices/jax_ops.py index 3d7e8367c90..489c413d7a5 100644 --- a/pennylane/devices/jax_ops.py +++ b/pennylane/devices/jax_ops.py @@ -183,16 +183,13 @@ def MultiRZ(theta, n): def SingleExcitation(phi): - r""" - Single excitation rotation. + r"""Single excitation rotation. Args: phi (float): rotation angle Returns: jnp.Tensor[float]: Single excitation rotation matrix - - """ c = jnp.cos(phi / 2) s = jnp.sin(phi / 2) @@ -200,15 +197,13 @@ def SingleExcitation(phi): def SingleExcitationPlus(phi): - r""" - Single excitation rotation with positive phase-shift outside the rotation subspace. + r"""Single excitation rotation with positive phase-shift outside the rotation subspace. Args: phi (float): rotation angle Returns: jnp.Tensor[complex]: Single excitation rotation matrix with positive phase-shift - """ c = jnp.cos(phi / 2) s = jnp.sin(phi / 2) @@ -217,15 +212,13 @@ def SingleExcitationPlus(phi): def SingleExcitationMinus(phi): - r""" - Single excitation rotation with negative phase-shift outside the rotation subspace. + r"""Single excitation rotation with negative phase-shift outside the rotation subspace. Args: phi (float): rotation angle Returns: tf.Tensor[complex]: Single excitation rotation matrix with negative phase-shift - """ c = jnp.cos(phi / 2) s = jnp.sin(phi / 2) diff --git a/pennylane/devices/tf_ops.py b/pennylane/devices/tf_ops.py index 97177dd1f3c..c2f841e7a8e 100644 --- a/pennylane/devices/tf_ops.py +++ b/pennylane/devices/tf_ops.py @@ -194,16 +194,13 @@ def CRot(a, b, c): def SingleExcitation(phi): - r""" - Single excitation rotation. + r"""Single excitation rotation. Args: phi (float): rotation angle Returns: tf.Tensor[float]: Single excitation rotation matrix - - """ phi = tf.cast(phi, dtype=C_DTYPE) c = tf.cos(phi / 2) @@ -212,15 +209,13 @@ def SingleExcitation(phi): def SingleExcitationPlus(phi): - r""" - Single excitation rotation with positive phase-shift outside the rotation subspace. + r"""Single excitation rotation with positive phase-shift outside the rotation subspace. Args: phi (float): rotation angle Returns: tf.Tensor[complex]: Single excitation rotation matrix with positive phase-shift - """ phi = tf.cast(phi, dtype=C_DTYPE) c = tf.cos(phi / 2) @@ -230,15 +225,13 @@ def SingleExcitationPlus(phi): def SingleExcitationMinus(phi): - r""" - Single excitation rotation with negative phase-shift outside the rotation subspace. + r"""Single excitation rotation with negative phase-shift outside the rotation subspace. Args: phi (float): rotation angle Returns: tf.Tensor[complex]: Single excitation rotation matrix with negative phase-shift - """ phi = tf.cast(phi, dtype=C_DTYPE) c = tf.cos(phi / 2) From 9ec2fd36af39aa4ee072a1ead77c4915462cb8c9 Mon Sep 17 00:00:00 2001 From: Josh Izaac Date: Mon, 22 Mar 2021 16:49:14 +0800 Subject: [PATCH 46/50] fix docs --- doc/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/conf.py b/doc/conf.py index 3e7e43a8509..f08189810df 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -46,7 +46,7 @@ "sphinx.ext.intersphinx", "sphinx_automodapi.automodapi", 'sphinx_copybutton', - # "m2r" + "m2r" ] source_suffix = ['.rst', '.md'] From 042f2b966352a524b14c6d0c8c9ec90034915a0b Mon Sep 17 00:00:00 2001 From: ixfoduap <40441298+ixfoduap@users.noreply.github.com> Date: Mon, 22 Mar 2021 14:20:30 -0400 Subject: [PATCH 47/50] Apply suggestions from code review Co-authored-by: antalszava --- .github/CHANGELOG.md | 2 +- tests/ops/test_qubit_ops.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 4f97775cfcf..cea603aa9d6 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -19,7 +19,7 @@ qml.SingleExcitation(phi, wires=[0, 1]) ``` - The `SingleExcitation` operation supports analytical gradients on hardware + The `SingleExcitation` operation supports analytic gradients on hardware using only four expectation value calculations, following results from [Kottmann et al.](https://arxiv.org/abs/2011.05938) diff --git a/tests/ops/test_qubit_ops.py b/tests/ops/test_qubit_ops.py index 59f5a6210d4..32d12684931 100644 --- a/tests/ops/test_qubit_ops.py +++ b/tests/ops/test_qubit_ops.py @@ -969,7 +969,7 @@ def test_tf(self, excitation, phi, diff_method): tensorflow interface""" tf = pytest.importorskip("tensorflow") - dev = qml.device('default.qubit', wires=2) + dev = qml.device('default.qubit.tf', wires=2) @qml.qnode(dev, interface="tf", diff_method=diff_method) def circuit(phi): @@ -998,7 +998,7 @@ def test_jax(self, excitation, phi, diff_method): jax = pytest.importorskip("jax") from jax import numpy as jnp - dev = qml.device('default.qubit', wires=2) + dev = qml.device('default.qubit.jax', wires=2) @qml.qnode(dev, interface="jax", diff_method=diff_method) def circuit(phi): From a1ef6f11393185977eb41ab756c4a0e8a2e816a2 Mon Sep 17 00:00:00 2001 From: ixfoduap <40441298+ixfoduap@users.noreply.github.com> Date: Mon, 22 Mar 2021 14:20:48 -0400 Subject: [PATCH 48/50] Update pennylane/devices/tf_ops.py --- pennylane/devices/tf_ops.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pennylane/devices/tf_ops.py b/pennylane/devices/tf_ops.py index c2f841e7a8e..8ef7afd1c7c 100644 --- a/pennylane/devices/tf_ops.py +++ b/pennylane/devices/tf_ops.py @@ -200,7 +200,8 @@ def SingleExcitation(phi): phi (float): rotation angle Returns: - tf.Tensor[float]: Single excitation rotation matrix + tf.Tensor[complex]: Single excitation rotation matrix + """ phi = tf.cast(phi, dtype=C_DTYPE) c = tf.cos(phi / 2) From 1d5fc6bc08445c0452bb204a16f134ec9dfebe96 Mon Sep 17 00:00:00 2001 From: ixfoduap Date: Mon, 22 Mar 2021 14:47:49 -0400 Subject: [PATCH 49/50] changes from code review --- tests/ops/test_qubit_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ops/test_qubit_ops.py b/tests/ops/test_qubit_ops.py index 2b2c7d3c66d..e1a53ba9894 100644 --- a/tests/ops/test_qubit_ops.py +++ b/tests/ops/test_qubit_ops.py @@ -947,6 +947,7 @@ def circuit(phi): return qml.state() assert np.allclose(state, circuit(np.pi / 2)) + @pytest.mark.parametrize("diff_method", ["parameter-shift", "backprop"]) @pytest.mark.parametrize(("excitation", "phi"), [(qml.SingleExcitation, -0.1), (qml.SingleExcitationPlus, 0.2), (qml.SingleExcitationMinus, np.pi/4)]) @@ -1000,7 +1001,6 @@ def test_jax(self, excitation, phi, diff_method): pytest.skip("JAX support for the parameter-shift method is still TBD") jax = pytest.importorskip("jax") - from jax import numpy as jnp dev = qml.device('default.qubit', wires=2) From ac42e67906429338adcea92d60ca0e4033402cd9 Mon Sep 17 00:00:00 2001 From: ixfoduap Date: Mon, 22 Mar 2021 16:04:06 -0400 Subject: [PATCH 50/50] fixes tests --- tests/ops/test_qubit_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ops/test_qubit_ops.py b/tests/ops/test_qubit_ops.py index babfebcccac..2098f87f9fb 100644 --- a/tests/ops/test_qubit_ops.py +++ b/tests/ops/test_qubit_ops.py @@ -947,7 +947,7 @@ def circuit(phi): @pytest.mark.parametrize(("excitation", "phi"), [(qml.SingleExcitation, -0.1), (qml.SingleExcitationPlus, 0.2), (qml.SingleExcitationMinus, np.pi/4)]) - def test_autograd_grad(self, excitation, phi): + def test_autograd_grad(self, diff_method, excitation, phi): """Tests that gradients are computed correctly using the autograd interface"""