Repository: PennyLaneAI/pennylane
Pull request #1390 (2021-06-08 10:37:24)

Ensure that the IsingXX and IsingZZ gates are differentiable
**Context:**
The gates were recently added `qml.IsingXX `in #1194 ,`qml.IsingZZ `in #1199 but there are at the moment not differentiable. This PR is directly related to the following issue https://github.com/PennyLaneAI/pennylane/issues/1386 .

**Description of the Change:**
1) I added functions representing these operations to `jax_ops.py,` `autograd_ops.py`, and `tf_ops.py`.
2) I modified the `DefaultQubitAutograd`, `DefaultQubitJAX`, and `DefaultQubitTF `devices to support these differentiable operations.
3) [Wip] Tests that ensure that the gradient of IsingXX and IsingZZ are correct for `diff_method="parameter-shift"` and `diff_method="backprop"`, for all ML interfaces (autograd, TF, JAX).

**Related issues**

Closes https://github.com/PennyLaneAI/pennylane/issues/1386.

codecov[bot]: # [Codecov](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1390?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) Report
> Merging [#1390](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1390?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) (bdcd051) into [master](https://codecov.io/gh/PennyLaneAI/pennylane/commit/0dcbed53b470eb7f6f93b54077e36ca6287235cf?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) (0dcbed5) will **increase** coverage by `0.00%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1390/graphs/tree.svg?width=650&height=150&src=pr&token=9S3YzlUgGk&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI)](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1390?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI)

```diff
@@           Coverage Diff           @@
##           master    #1390   +/-   ##
=======================================
  Coverage   98.18%   98.18%           
=======================================
  Files         156      156           
  Lines       11685    11708   +23     
=======================================
+ Hits        11473    11496   +23     
  Misses        212      212           
```


| [Impacted Files](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1390?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) | Coverage Δ | |
|---|---|---|
| [pennylane/devices/default\_qubit\_autograd.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1390/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL2RldmljZXMvZGVmYXVsdF9xdWJpdF9hdXRvZ3JhZC5weQ==) | `100.00% <ø> (ø)` | |
| [pennylane/devices/default\_qubit\_jax.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1390/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL2RldmljZXMvZGVmYXVsdF9xdWJpdF9qYXgucHk=) | `94.36% <ø> (ø)` | |
| [pennylane/devices/default\_qubit\_tf.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1390/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL2RldmljZXMvZGVmYXVsdF9xdWJpdF90Zi5weQ==) | `90.66% <ø> (ø)` | |
| [pennylane/devices/autograd\_ops.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1390/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL2RldmljZXMvYXV0b2dyYWRfb3BzLnB5) | `100.00% <100.00%> (ø)` | |
| [pennylane/devices/jax\_ops.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1390/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL2RldmljZXMvamF4X29wcy5weQ==) | `100.00% <100.00%> (ø)` | |
| [pennylane/devices/tf\_ops.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1390/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL2RldmljZXMvdGZfb3BzLnB5) | `100.00% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1390?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1390?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI). Last update [0dcbed5...bdcd051](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1390?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).


Patch:
diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md
index fb7ca83da..2fdac6772 100644
--- a/.github/CHANGELOG.md
+++ b/.github/CHANGELOG.md
@@ -577,6 +577,9 @@ random_mat2 = rng.standard_normal(3, requires_grad=False)
 
 <h3>Bug fixes</h3>
 
+* Fixes the differentiability of the operations `IsingXX` and `IsingZZ` for Autograd, Jax and Tensorflow.
+[(#1390)](https://github.com/PennyLaneAI/pennylane/pull/1390)
+
 * Fixes a bug where multiple identical Hamiltonian terms will produce a
   different result with ``optimize=True`` using ``ExpvalCost``.
   [(#1405)](https://github.com/XanaduAI/pennylane/pull/1405)
@@ -647,8 +650,9 @@ random_mat2 = rng.standard_normal(3, requires_grad=False)
 This release contains contributions from (in alphabetical order):
 
 Marius Aglitoiu, Vishnu Ajith, Thomas Bromley, Jack Ceroni, Alaric Cheng, Miruna Daian, Olivia Di Matteo,
+
 Tanya Garg, Christian Gogolin, Diego Guala, Anthony Hayes, Ryan Hill, Josh Izaac, Pavan Jayasinha, Nathan Killoran, 
-Christina Lee, Ryan Levy, Nahum Sá, Maria Schuld, Johannes Jakob Meyer, Romain Moyard, Brian Shi, Antal Száva,
+Christina Lee, Ryan Levy, Johannes Jakob Meyer, Romain Moyard, Nahum Sá, Maria Schuld, Brian Shi, Antal Száva,
 David Wierichs, Vincent Wong, Alberto Maldonado, Ashish Panigrahi.
 
 
diff --git a/pennylane/devices/autograd_ops.py b/pennylane/devices/autograd_ops.py
index cc1a611e0..ee4b1e0f8 100644
--- a/pennylane/devices/autograd_ops.py
+++ b/pennylane/devices/autograd_ops.py
@@ -28,6 +28,7 @@ Z = np.array([[1, 0], [0, -1]], dtype=C_DTYPE)
 
 II = np.eye(4, dtype=C_DTYPE)
 ZZ = np.array(kron(Z, Z), dtype=C_DTYPE)
+XX = np.array(kron(X, X), dtype=C_DTYPE)
 
 IX = np.array(kron(I, X), dtype=C_DTYPE)
 IY = np.array(kron(I, Y), dtype=C_DTYPE)
@@ -182,6 +183,44 @@ def MultiRZ(theta, n):
     return np.exp(-1j * theta / 2 * pauli_eigs(n))
 
 
+def IsingXX(phi):
+    r"""Ising XX coupling gate
+
+    .. math:: XX(\phi) = \begin{bmatrix}
+        \cos(\phi / 2) & 0 & 0 & -i \sin(\phi / 2) \\
+        0 & \cos(\phi / 2) & -i \sin(\phi / 2) & 0 \\
+        0 & -i \sin(\phi / 2) & \cos(\phi / 2) & 0 \\
+        -i \sin(\phi / 2) & 0 & 0 & \cos(\phi / 2)
+        \end{bmatrix}.
+
+    Args:
+        phi (float): rotation angle :math:`\phi`
+    Returns:
+        array[complex]: unitary 4x4 rotation matrix
+    """
+    return np.cos(phi / 2) * II - 1j * np.sin(phi / 2) * XX
+
+
+def IsingZZ(phi):
+    r"""Ising ZZ coupling gate
+
+    .. math:: ZZ(\phi) = \begin{bmatrix}
+        e^{-i \phi / 2} & 0 & 0 & 0 \\
+        0 & e^{i \phi / 2} & 0 & 0 \\
+        0 & 0 & e^{i \phi / 2} & 0 \\
+        0 & 0 & 0 & e^{-i \phi / 2}
+        \end{bmatrix}.
+
+    Args:
+        phi (float): rotation angle :math:`\phi`
+    Returns:
+        array[complex]: unitary 4x4 rotation matrix
+    """
+    e_m = np.exp(-1j * phi / 2)
+    e = np.exp(1j * phi / 2)
+    return np.array([[e_m, 0, 0, 0], [0, e, 0, 0], [0, 0, e, 0], [0, 0, 0, e_m]])
+
+
 def SingleExcitation(phi):
     r"""Single excitation rotation.
 
diff --git a/pennylane/devices/default_qubit_autograd.py b/pennylane/devices/default_qubit_autograd.py
index 8571b8634..d06d383f4 100644
--- a/pennylane/devices/default_qubit_autograd.py
+++ b/pennylane/devices/default_qubit_autograd.py
@@ -94,6 +94,8 @@ class DefaultQubitAutograd(DefaultQubit):
         "CRZ": autograd_ops.CRZ,
         "CRot": autograd_ops.CRot,
         "MultiRZ": autograd_ops.MultiRZ,
+        "IsingXX": autograd_ops.IsingXX,
+        "IsingZZ": autograd_ops.IsingZZ,
         "SingleExcitation": autograd_ops.SingleExcitation,
         "SingleExcitationPlus": autograd_ops.SingleExcitationPlus,
         "SingleExcitationMinus": autograd_ops.SingleExcitationMinus,
diff --git a/pennylane/devices/default_qubit_jax.py b/pennylane/devices/default_qubit_jax.py
index 3d99a1928..67913669a 100644
--- a/pennylane/devices/default_qubit_jax.py
+++ b/pennylane/devices/default_qubit_jax.py
@@ -147,6 +147,8 @@ class DefaultQubitJax(DefaultQubit):
         "CRZ": jax_ops.CRZ,
         "CRot": jax_ops.CRot,
         "MultiRZ": jax_ops.MultiRZ,
+        "IsingXX": jax_ops.IsingXX,
+        "IsingZZ": jax_ops.IsingZZ,
         "SingleExcitation": jax_ops.SingleExcitation,
         "SingleExcitationPlus": jax_ops.SingleExcitationPlus,
         "SingleExcitationMinus": jax_ops.SingleExcitationMinus,
diff --git a/pennylane/devices/default_qubit_tf.py b/pennylane/devices/default_qubit_tf.py
index b60281d7f..df0dba8dd 100644
--- a/pennylane/devices/default_qubit_tf.py
+++ b/pennylane/devices/default_qubit_tf.py
@@ -141,6 +141,8 @@ class DefaultQubitTF(DefaultQubit):
         "CRY": tf_ops.CRY,
         "CRZ": tf_ops.CRZ,
         "CRot": tf_ops.CRot,
+        "IsingXX": tf_ops.IsingXX,
+        "IsingZZ": tf_ops.IsingZZ,
         "SingleExcitation": tf_ops.SingleExcitation,
         "SingleExcitationPlus": tf_ops.SingleExcitationPlus,
         "SingleExcitationMinus": tf_ops.SingleExcitationMinus,
diff --git a/pennylane/devices/jax_ops.py b/pennylane/devices/jax_ops.py
index dae1c675b..9d9555ae7 100644
--- a/pennylane/devices/jax_ops.py
+++ b/pennylane/devices/jax_ops.py
@@ -28,6 +28,7 @@ Z = jnp.array([[1, 0], [0, -1]], dtype=C_DTYPE)
 
 II = jnp.eye(4, dtype=C_DTYPE)
 ZZ = jnp.array(jnp.kron(Z, Z), dtype=C_DTYPE)
+XX = jnp.array(jnp.kron(X, X), dtype=C_DTYPE)
 
 IX = jnp.array(jnp.kron(I, X), dtype=C_DTYPE)
 IY = jnp.array(jnp.kron(I, Y), dtype=C_DTYPE)
@@ -182,6 +183,44 @@ def MultiRZ(theta, n):
     return jnp.exp(-1j * theta / 2 * pauli_eigs(n))
 
 
+def IsingXX(phi):
+    r"""Ising XX coupling gate.
+
+    .. math:: XX(\phi) = \begin{bmatrix}
+        \cos(\phi / 2) & 0 & 0 & -i \sin(\phi / 2) \\
+        0 & \cos(\phi / 2) & -i \sin(\phi / 2) & 0 \\
+        0 & -i \sin(\phi / 2) & \cos(\phi / 2) & 0 \\
+        -i \sin(\phi / 2) & 0 & 0 & \cos(\phi / 2)
+        \end{bmatrix}.
+
+    Args:
+        phi (float): rotation angle :math:`\phi`
+    Returns:
+        array[complex]: unitary 4x4 rotation matrix
+    """
+    return jnp.cos(phi / 2) * II - 1j * jnp.sin(phi / 2) * XX
+
+
+def IsingZZ(phi):
+    r"""Ising ZZ coupling gate
+
+    .. math:: ZZ(\phi) = \begin{bmatrix}
+        e^{-i \phi / 2} & 0 & 0 & 0 \\
+        0 & e^{i \phi / 2} & 0 & 0 \\
+        0 & 0 & e^{i \phi / 2} & 0 \\
+        0 & 0 & 0 & e^{-i \phi / 2}
+        \end{bmatrix}.
+
+    Args:
+        phi (float): rotation angle :math:`\phi`
+    Returns:
+        array[complex]: unitary 4x4 rotation matrix
+    """
+    e_m = jnp.exp(-1j * phi / 2)
+    e = jnp.exp(1j * phi / 2)
+    return jnp.array([[e_m, 0, 0, 0], [0, e, 0, 0], [0, 0, e, 0], [0, 0, 0, e_m]])
+
+
 def SingleExcitation(phi):
     r"""Single excitation rotation.
 
diff --git a/pennylane/devices/tf_ops.py b/pennylane/devices/tf_ops.py
index ff3c8a700..febaa6c8d 100644
--- a/pennylane/devices/tf_ops.py
+++ b/pennylane/devices/tf_ops.py
@@ -28,6 +28,7 @@ Z = tf.constant([[1, 0], [0, -1]], dtype=C_DTYPE)
 
 II = tf.eye(4, dtype=C_DTYPE)
 ZZ = tf.constant(kron(Z, Z), dtype=C_DTYPE)
+XX = tf.constant(kron(X, X), dtype=C_DTYPE)
 
 IX = tf.constant(kron(I, X), dtype=C_DTYPE)
 IY = tf.constant(kron(I, Y), dtype=C_DTYPE)
@@ -193,6 +194,46 @@ def CRot(a, b, c):
     return tf.linalg.diag(CRZ(c)) @ (CRY(b) @ tf.linalg.diag(CRZ(a)))
 
 
+def IsingXX(phi):
+    r"""Ising XX coupling gate
+
+    .. math:: XX(\phi) = \begin{bmatrix}
+        \cos(\phi / 2) & 0 & 0 & -i \sin(\phi / 2) \\
+        0 & \cos(\phi / 2) & -i \sin(\phi / 2) & 0 \\
+        0 & -i \sin(\phi / 2) & \cos(\phi / 2) & 0 \\
+        -i \sin(\phi / 2) & 0 & 0 & \cos(\phi / 2)
+        \end{bmatrix}.
+
+    Args:
+        phi (float): rotation angle :math:`\phi`
+    Returns:
+        tf.Tensor[complex]: unitary 4x4 rotation matrix
+    """
+    phi = tf.cast(phi, dtype=C_DTYPE)
+    return tf.cos(phi / 2) * II - 1j * tf.sin(phi / 2) * XX
+
+
+def IsingZZ(phi):
+    r"""Ising ZZ coupling gate
+
+    .. math:: ZZ(\phi) = \begin{bmatrix}
+        e^{-i \phi / 2} & 0 & 0 & 0 \\
+        0 & e^{i \phi / 2} & 0 & 0 \\
+        0 & 0 & e^{i \phi / 2} & 0 \\
+        0 & 0 & 0 & e^{-i \phi / 2}
+        \end{bmatrix}.
+
+    Args:
+        phi (float): rotation :math:`\phi`
+    Returns:
+        tf.Tensor[complex]: unitary 4x4 rotation matrix
+    """
+    phi = tf.cast(phi, dtype=C_DTYPE)
+    e_m = tf.exp(-1j * phi / 2)
+    e = tf.exp(1j * phi / 2)
+    return tf.convert_to_tensor([[e_m, 0, 0, 0], [0, e, 0, 0], [0, 0, e, 0], [0, 0, 0, e_m]])
+
+
 def SingleExcitation(phi):
     r"""Single excitation rotation.
 
diff --git a/pennylane/ops/qubit.py b/pennylane/ops/qubit.py
index 97fd3ce75..de82446bd 100644
--- a/pennylane/ops/qubit.py
+++ b/pennylane/ops/qubit.py
@@ -1797,6 +1797,7 @@ class IsingXX(Operation):
     num_wires = 2
     par_domain = "R"
     grad_method = "A"
+    generator = [np.array([[0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0]]), -1 / 2]
 
     @classmethod
     def _matrix(cls, *params):
@@ -1848,6 +1849,7 @@ class IsingZZ(Operation):
     num_wires = 2
     par_domain = "R"
     grad_method = "A"
+    generator = [np.array([[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]]), -1 / 2]
 
     @staticmethod
     def decomposition(phi, wires):


Test patch:
diff --git a/tests/ops/test_qubit_ops.py b/tests/ops/test_qubit_ops.py
index ed4ca06b0..c19e9c4cf 100644
--- a/tests/ops/test_qubit_ops.py
+++ b/tests/ops/test_qubit_ops.py
@@ -22,6 +22,7 @@ import numpy as np
 from numpy.linalg import multi_dot
 from scipy.stats import unitary_group
 from scipy.linalg import expm
+from pennylane import numpy as npp
 
 import pennylane as qml
 from pennylane.wires import Wires
@@ -777,6 +778,241 @@ class TestOperations:
 
         assert np.allclose(decomposed_matrix, op.matrix, atol=tol, rtol=0)
 
+    device_methods = [
+        ["default.qubit", "finite-diff"],
+        ["default.qubit", "parameter-shift"],
+        ["default.qubit", "backprop"],
+        ["default.qubit", "adjoint"],
+    ]
+
+    phis = [0.1, 0.2, 0.3]
+
+    configuration = []
+
+    for phi in phis:
+        for device, method in device_methods:
+            configuration.append([device, method, phi])
+
+    @pytest.mark.parametrize("dev_name,diff_method,phi", configuration)
+    def test_isingxx_autograd_grad(self, tol, dev_name, diff_method, phi):
+        """Test the gradient for the gate IsingXX."""
+        dev = qml.device(dev_name, wires=2)
+
+        psi_0 = 0.1
+        psi_1 = 0.2
+        psi_2 = 0.3
+        psi_3 = 0.4
+
+        init_state = npp.array([psi_0, psi_1, psi_2, psi_3], requires_grad=False)
+        norm = np.linalg.norm(init_state)
+        init_state /= norm
+
+        @qml.qnode(dev, diff_method=diff_method, interface="autograd")
+        def circuit(phi):
+            qml.QubitStateVector(init_state, wires=[0, 1])
+            qml.IsingXX(phi, wires=[0, 1])
+            return qml.expval(qml.PauliZ(0))
+
+        phi = npp.array(0.1, requires_grad=True)
+
+        expected = (
+            0.5
+            * (1 / norm ** 2)
+            * (
+                -np.sin(phi) * (psi_0 ** 2 + psi_1 ** 2 - psi_2 ** 2 - psi_3 ** 2)
+                + 2
+                * np.sin(phi / 2)
+                * np.cos(phi / 2)
+                * (-(psi_0 ** 2) - psi_1 ** 2 + psi_2 ** 2 + psi_3 ** 2)
+            )
+        )
+
+        res = qml.grad(circuit)(phi)
+        assert np.allclose(res, expected, atol=tol, rtol=0)
+
+    @pytest.mark.parametrize("dev_name,diff_method,phi", configuration)
+    def test_isingzz_autograd_grad(self, tol, dev_name, diff_method, phi):
+        """Test the gradient for the gate IsingZZ."""
+        dev = qml.device(dev_name, wires=2)
+
+        psi_0 = 0.1
+        psi_1 = 0.2
+        psi_2 = 0.3
+        psi_3 = 0.4
+
+        init_state = npp.array([psi_0, psi_1, psi_2, psi_3], requires_grad=False)
+        norm = np.linalg.norm(init_state)
+        init_state /= norm
+
+        @qml.qnode(dev, diff_method=diff_method, interface="autograd")
+        def circuit(phi):
+            qml.QubitStateVector(init_state, wires=[0, 1])
+            qml.IsingZZ(phi, wires=[0, 1])
+            return qml.expval(qml.PauliX(0))
+
+        phi = npp.array(0.1, requires_grad=True)
+
+        expected = (1 / norm ** 2) * (-2 * (psi_0 * psi_2 + psi_1 * psi_3) * np.sin(phi))
+
+        res = qml.grad(circuit)(phi)
+        assert np.allclose(res, expected, atol=tol, rtol=0)
+
+    @pytest.mark.parametrize("dev_name,diff_method,phi", configuration)
+    def test_isingxx_jax_grad(self, tol, dev_name, diff_method, phi):
+        """Test the gradient for the gate IsingXX."""
+
+        if diff_method in {"finite-diff"}:
+            pytest.skip("Test does not support finite-diff")
+
+        if diff_method in {"parameter-shift"}:
+            pytest.skip("Test does not support parameter-shift")
+
+        jax = pytest.importorskip("jax")
+        jnp = pytest.importorskip("jax.numpy")
+
+        dev = qml.device(dev_name, wires=2)
+
+        psi_0 = 0.1
+        psi_1 = 0.2
+        psi_2 = 0.3
+        psi_3 = 0.4
+
+        init_state = jnp.array([psi_0, psi_1, psi_2, psi_3])
+        norm = jnp.linalg.norm(init_state)
+        init_state = init_state / norm
+
+        @qml.qnode(dev, diff_method=diff_method, interface="jax")
+        def circuit(phi):
+            qml.QubitStateVector(init_state, wires=[0, 1])
+            qml.IsingXX(phi, wires=[0, 1])
+            return qml.expval(qml.PauliZ(0))
+
+        phi = jnp.array(0.1)
+
+        expected = (
+            0.5
+            * (1 / norm ** 2)
+            * (
+                -np.sin(phi) * (psi_0 ** 2 + psi_1 ** 2 - psi_2 ** 2 - psi_3 ** 2)
+                + 2
+                * np.sin(phi / 2)
+                * np.cos(phi / 2)
+                * (-(psi_0 ** 2) - psi_1 ** 2 + psi_2 ** 2 + psi_3 ** 2)
+            )
+        )
+
+        res = jax.grad(circuit, argnums=0)(phi)
+        assert np.allclose(res, expected, atol=tol, rtol=0)
+
+    @pytest.mark.parametrize("dev_name,diff_method,phi", configuration)
+    def test_isingzz_jax_grad(self, tol, dev_name, diff_method, phi):
+        """Test the gradient for the gate IsingZZ."""
+
+        if diff_method in {"finite-diff"}:
+            pytest.skip("Test does not support finite-diff")
+
+        if diff_method in {"parameter-shift"}:
+            pytest.skip("Test does not support parameter-shift")
+
+        jax = pytest.importorskip("jax")
+        jnp = pytest.importorskip("jax.numpy")
+
+        dev = qml.device(dev_name, wires=2)
+
+        psi_0 = 0.1
+        psi_1 = 0.2
+        psi_2 = 0.3
+        psi_3 = 0.4
+
+        init_state = jnp.array([psi_0, psi_1, psi_2, psi_3])
+        norm = jnp.linalg.norm(init_state)
+        init_state = init_state / norm
+
+        @qml.qnode(dev, diff_method=diff_method, interface="jax")
+        def circuit(phi):
+            qml.QubitStateVector(init_state, wires=[0, 1])
+            qml.IsingZZ(phi, wires=[0, 1])
+            return qml.expval(qml.PauliX(0))
+
+        phi = jnp.array(0.1)
+
+        expected = (1 / norm ** 2) * (-2 * (psi_0 * psi_2 + psi_1 * psi_3) * np.sin(phi))
+
+        res = jax.grad(circuit, argnums=0)(phi)
+        assert np.allclose(res, expected, atol=tol, rtol=0)
+
+    @pytest.mark.parametrize("dev_name,diff_method,phi", configuration)
+    def test_isingxx_tf_grad(self, tol, dev_name, diff_method, phi):
+        """Test the gradient for the gate IsingXX."""
+        tf = pytest.importorskip("tensorflow", minversion="2.1")
+
+        dev = qml.device(dev_name, wires=2)
+
+        psi_0 = tf.Variable(0.1, dtype=tf.complex128)
+        psi_1 = tf.Variable(0.2, dtype=tf.complex128)
+        psi_2 = tf.Variable(0.3, dtype=tf.complex128)
+        psi_3 = tf.Variable(0.4, dtype=tf.complex128)
+
+        init_state = tf.Variable([psi_0, psi_1, psi_2, psi_3], dtype=tf.complex128)
+        norm = tf.norm(init_state)
+        init_state = init_state / norm
+
+        @qml.qnode(dev, interface="tf", diff_method=diff_method)
+        def circuit(phi):
+            qml.QubitStateVector(init_state, wires=[0, 1])
+            qml.IsingXX(phi, wires=[0, 1])
+            return qml.expval(qml.PauliZ(0))
+
+        phi = tf.Variable(0.1, dtype=tf.complex128)
+
+        expected = (
+            0.5
+            * (1 / norm ** 2)
+            * (
+                -tf.sin(phi) * (psi_0 ** 2 + psi_1 ** 2 - psi_2 ** 2 - psi_3 ** 2)
+                + 2
+                * tf.sin(phi / 2)
+                * tf.cos(phi / 2)
+                * (-(psi_0 ** 2) - psi_1 ** 2 + psi_2 ** 2 + psi_3 ** 2)
+            )
+        )
+
+        with tf.GradientTape() as tape:
+            result = circuit(phi)
+        res = tape.gradient(result, phi)
+        assert np.allclose(res, expected, atol=tol, rtol=0)
+
+    @pytest.mark.parametrize("dev_name,diff_method,phi", configuration)
+    def test_isingzz_tf_grad(self, tol, dev_name, diff_method, phi):
+        """Test the gradient for the gate IsingZZ."""
+        tf = pytest.importorskip("tensorflow", minversion="2.1")
+
+        dev = qml.device(dev_name, wires=2)
+
+        psi_0 = tf.Variable(0.1, dtype=tf.complex128)
+        psi_1 = tf.Variable(0.2, dtype=tf.complex128)
+        psi_2 = tf.Variable(0.3, dtype=tf.complex128)
+        psi_3 = tf.Variable(0.4, dtype=tf.complex128)
+
+        init_state = tf.Variable([psi_0, psi_1, psi_2, psi_3], dtype=tf.complex128)
+        norm = tf.norm(init_state)
+        init_state = init_state / norm
+
+        @qml.qnode(dev, interface="tf", diff_method=diff_method)
+        def circuit(phi):
+            qml.QubitStateVector(init_state, wires=[0, 1])
+            qml.IsingZZ(phi, wires=[0, 1])
+            return qml.expval(qml.PauliX(0))
+
+        phi = tf.Variable(0.1, dtype=tf.complex128)
+
+        expected = (1 / norm ** 2) * (-2 * (psi_0 * psi_2 + psi_1 * psi_3) * np.sin(phi))
+
+        with tf.GradientTape() as tape:
+            result = circuit(phi)
+        res = tape.gradient(result, phi)
+        assert np.allclose(res, expected, atol=tol, rtol=0)
+
     def test_isingzz_decomposition(self, tol):
         """Tests that the decomposition of the IsingZZ gate is correct"""
         param = 0.1234