diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md
index 96613c882f1..dfb90a6acfb 100644
--- a/.github/CHANGELOG.md
+++ b/.github/CHANGELOG.md
@@ -2,6 +2,9 @@
New features since last release
+* Added a new `SISWAP` operation and a `SQISW` alias with support to the `default_qubit` device.
+ [#1563](https://github.com/PennyLaneAI/pennylane/pull/1563)
+
* The `RotosolveOptimizer` now can tackle general parametrized circuits, and is no longer
restricted to single-qubit Pauli rotations.
[(#1489)](https://github.com/PennyLaneAI/pennylane/pull/1489)
@@ -262,7 +265,7 @@ and requirements-ci.txt (unpinned). This latter would be used by the CI.
This release contains contributions from (in alphabetical order):
-Akash Narayanan B, Thomas Bromley, Tanya Garg, Josh Izaac, Prateek Jain, Johannes Jakob Meyer, Maria Schuld,
+Vishnu Ajith, Akash Narayanan B, Thomas Bromley, Tanya Garg, Josh Izaac, Prateek Jain, Johannes Jakob Meyer, Maria Schuld,
Ingrid Strandberg, David Wierichs, Vincent Wong.
diff --git a/doc/introduction/operations.rst b/doc/introduction/operations.rst
index 8a1a63894e8..0e08d343356 100644
--- a/doc/introduction/operations.rst
+++ b/doc/introduction/operations.rst
@@ -73,6 +73,8 @@ Qubit gates
~pennylane.CY
~pennylane.SWAP
~pennylane.ISWAP
+ ~pennylane.SISWAP
+ ~pennylane.SQISW
~pennylane.IsingXX
~pennylane.IsingYY
~pennylane.IsingZZ
diff --git a/pennylane/devices/default_qubit.py b/pennylane/devices/default_qubit.py
index ea8a60acfce..404e6c3d685 100644
--- a/pennylane/devices/default_qubit.py
+++ b/pennylane/devices/default_qubit.py
@@ -108,6 +108,8 @@ class DefaultQubit(QubitDevice):
"CNOT",
"SWAP",
"ISWAP",
+ "SISWAP",
+ "SQISW",
"CSWAP",
"Toffoli",
"CY",
diff --git a/pennylane/ops/qubit/__init__.py b/pennylane/ops/qubit/__init__.py
index e9a23e95ea2..729338fb184 100644
--- a/pennylane/ops/qubit/__init__.py
+++ b/pennylane/ops/qubit/__init__.py
@@ -52,6 +52,8 @@
"CY",
"SWAP",
"ISWAP",
+ "SISWAP",
+ "SQISW",
"CSWAP",
"Toffoli",
"RX",
diff --git a/pennylane/ops/qubit/non_parametric_ops.py b/pennylane/ops/qubit/non_parametric_ops.py
index 24fd38452ce..eb019773ed4 100644
--- a/pennylane/ops/qubit/non_parametric_ops.py
+++ b/pennylane/ops/qubit/non_parametric_ops.py
@@ -646,6 +646,69 @@ def adjoint(self):
return ISWAP(wires=self.wires).inv()
+class SISWAP(Operation):
+ r"""SISWAP(wires)
+ The square root of i-swap operator. Can also be accessed as ``qml.SQISW``
+
+ .. math:: SISWAP = \begin{bmatrix}
+ 1 & 0 & 0 & 0 \\
+ 0 & 1/ \sqrt{2} & i/\sqrt{2} & 0\\
+ 0 & i/ \sqrt{2} & 1/ \sqrt{2} & 0\\
+ 0 & 0 & 0 & 1
+ \end{bmatrix}.
+
+ **Details:**
+
+ * Number of wires: 2
+ * Number of parameters: 0
+
+ Args:
+ wires (Sequence[int]): the wires the operation acts on
+ """
+ num_params = 0
+ num_wires = 2
+ par_domain = None
+
+ @classmethod
+ def _matrix(cls, *params):
+ return np.array(
+ [
+ [1, 0, 0, 0],
+ [0, INV_SQRT2, INV_SQRT2 * 1j, 0],
+ [0, INV_SQRT2 * 1j, INV_SQRT2, 0],
+ [0, 0, 0, 1],
+ ]
+ )
+
+ @classmethod
+ def _eigvals(cls, *params):
+ return np.array([INV_SQRT2 * (1 + 1j), INV_SQRT2 * (1 - 1j), 1, 1])
+
+ @staticmethod
+ def decomposition(wires):
+ decomp_ops = [
+ SX(wires=wires[0]),
+ qml.RZ(np.pi / 2, wires=wires[0]),
+ CNOT(wires=[wires[0], wires[1]]),
+ SX(wires=wires[0]),
+ qml.RZ(7 * np.pi / 4, wires=wires[0]),
+ SX(wires=wires[0]),
+ qml.RZ(np.pi / 2, wires=wires[0]),
+ SX(wires=wires[1]),
+ qml.RZ(7 * np.pi / 4, wires=wires[1]),
+ CNOT(wires=[wires[0], wires[1]]),
+ SX(wires=wires[0]),
+ SX(wires=wires[1]),
+ ]
+ return decomp_ops
+
+ def adjoint(self):
+ return SISWAP(wires=self.wires).inv()
+
+
+SQISW = SISWAP
+
+
class CSWAP(Operation):
r"""CSWAP(wires)
The controlled-swap operator
diff --git a/tests/devices/test_default_qubit.py b/tests/devices/test_default_qubit.py
index c5602efb12f..8498dc82d04 100644
--- a/tests/devices/test_default_qubit.py
+++ b/tests/devices/test_default_qubit.py
@@ -224,7 +224,57 @@ def test_apply_operation_single_wire_no_parameters_inverse(
),
]
- all_two_wires_no_parameters = test_data_two_wires_no_parameters + test_data_iswap
+ test_data_siswap = [
+ (qml.SISWAP, [1, 0, 0, 0], [1, 0, 0, 0]),
+ (qml.SISWAP, [0, 1, 0, 0], [0, 1 / math.sqrt(2), 1 / math.sqrt(2) * 1j, 0]),
+ (
+ qml.SISWAP,
+ [1 / math.sqrt(2), 1 / math.sqrt(2), 0, 0],
+ [1 / math.sqrt(2), 0.5, 0.5 * 1j, 0],
+ ),
+ ]
+
+ test_data_sqisw = [
+ (qml.SQISW, [1, 0, 0, 0], [1, 0, 0, 0]),
+ (qml.SQISW, [0, 1, 0, 0], [0, 1 / math.sqrt(2), 1 / math.sqrt(2) * 1j, 0]),
+ (
+ qml.SQISW,
+ [1 / math.sqrt(2), 1 / math.sqrt(2), 0, 0],
+ [1 / math.sqrt(2), 0.5, 0.5 * 1j, 0],
+ ),
+ ]
+
+ test_data_siswap_inv = [
+ (
+ qml.SISWAP,
+ [1 / math.sqrt(2), 0, 1 / math.sqrt(2), 0],
+ [1 / math.sqrt(2), -0.5 * 1j, 0.5, 0],
+ ),
+ (qml.SISWAP, [0, 0, 1, 0], [0, -1 / math.sqrt(2) * 1j, 1 / math.sqrt(2), 0]),
+ (
+ qml.SISWAP,
+ [1 / math.sqrt(2), 0, -1 / math.sqrt(2), 0],
+ [1 / math.sqrt(2), 0.5 * 1j, -0.5, 0],
+ ),
+ ]
+
+ test_data_sqisw_inv = [
+ (
+ qml.SQISW,
+ [1 / math.sqrt(2), 0, 1 / math.sqrt(2), 0],
+ [1 / math.sqrt(2), -0.5 * 1j, 0.5, 0],
+ ),
+ (qml.SQISW, [0, 0, 1, 0], [0, -1 / math.sqrt(2) * 1j, 1 / math.sqrt(2), 0]),
+ (
+ qml.SQISW,
+ [1 / math.sqrt(2), 0, -1 / math.sqrt(2), 0],
+ [1 / math.sqrt(2), 0.5 * 1j, -0.5, 0],
+ ),
+ ]
+
+ all_two_wires_no_parameters = (
+ test_data_two_wires_no_parameters + test_data_iswap + test_data_siswap + test_data_sqisw
+ )
@pytest.mark.parametrize("operation,input,expected_output", all_two_wires_no_parameters)
def test_apply_operation_two_wires_no_parameters(
@@ -240,7 +290,12 @@ def test_apply_operation_two_wires_no_parameters(
qubit_device_2_wires._state.flatten(), np.array(expected_output), atol=tol, rtol=0
)
- all_two_wires_no_parameters_inv = test_data_two_wires_no_parameters + test_data_iswap_inv
+ all_two_wires_no_parameters_inv = (
+ test_data_two_wires_no_parameters
+ + test_data_iswap_inv
+ + test_data_siswap_inv
+ + test_data_sqisw_inv
+ )
@pytest.mark.parametrize("operation,input,expected_output", all_two_wires_no_parameters_inv)
def test_apply_operation_two_wires_no_parameters_inverse(
diff --git a/tests/gate_data.py b/tests/gate_data.py
index dfae7c13ff4..9000f975742 100644
--- a/tests/gate_data.py
+++ b/tests/gate_data.py
@@ -23,6 +23,14 @@
CNOT = np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]]) #: CNOT gate
SWAP = np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]]) #: SWAP gate
ISWAP = np.array([[1, 0, 0, 0], [0, 0, 1j, 0], [0, 1j, 0, 0], [0, 0, 0, 1]]) #: ISWAP gate
+SISWAP = np.array(
+ [
+ [1, 0, 0, 0],
+ [0, 1 / math.sqrt(2), 1 / math.sqrt(2) * 1j, 0],
+ [0, 1 / math.sqrt(2) * 1j, 1 / math.sqrt(2), 0],
+ [0, 0, 0, 1],
+ ]
+)
CZ = np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, -1]]) #: CZ gate
S = np.array([[1, 0], [0, 1j]]) #: Phase Gate
T = np.array([[1, 0], [0, cmath.exp(1j * np.pi / 4)]]) #: T Gate
diff --git a/tests/ops/test_qubit_ops.py b/tests/ops/test_qubit_ops.py
index d71d4740057..5f2df5830f0 100644
--- a/tests/ops/test_qubit_ops.py
+++ b/tests/ops/test_qubit_ops.py
@@ -39,6 +39,7 @@
CNOT,
SWAP,
ISWAP,
+ SISWAP,
CZ,
S,
T,
@@ -400,6 +401,7 @@ def circuit(basis_state):
(qml.CNOT, CNOT),
(qml.SWAP, SWAP),
(qml.ISWAP, ISWAP),
+ (qml.SISWAP, SISWAP),
(qml.CZ, CZ),
(qml.S, S),
(qml.T, T),
@@ -419,6 +421,8 @@ def circuit(basis_state):
qml.CY(wires=[0, 1]),
qml.SWAP(wires=[0, 1]),
qml.ISWAP(wires=[0, 1]),
+ qml.SISWAP(wires=[0, 1]),
+ qml.SQISW(wires=[0, 1]),
qml.CSWAP(wires=[0, 1, 2]),
qml.PauliRot(0.123, "Y", wires=0),
qml.IsingXX(0.123, wires=[0, 1]),
@@ -772,6 +776,55 @@ def test_ISWAP_decomposition(self, tol):
assert np.allclose(decomposed_matrix, op.matrix, atol=tol, rtol=0)
+ @pytest.mark.parametrize("siswap_op", [qml.SISWAP, qml.SQISW])
+ def test_SISWAP_decomposition(self, siswap_op, tol):
+ """Tests that the decomposition of the SISWAP gate and its SQISW alias gate is correct"""
+ op = siswap_op(wires=[0, 1])
+ res = op.decomposition(op.wires)
+
+ assert len(res) == 12
+
+ assert res[0].wires == Wires([0])
+ assert res[1].wires == Wires([0])
+ assert res[2].wires == Wires([0, 1])
+ assert res[3].wires == Wires([0])
+ assert res[4].wires == Wires([0])
+ assert res[5].wires == Wires([0])
+ assert res[6].wires == Wires([0])
+ assert res[7].wires == Wires([1])
+ assert res[8].wires == Wires([1])
+ assert res[9].wires == Wires([0, 1])
+ assert res[10].wires == Wires([0])
+ assert res[11].wires == Wires([1])
+
+ assert res[0].name == "SX"
+ assert res[1].name == "RZ"
+ assert res[2].name == "CNOT"
+ assert res[3].name == "SX"
+ assert res[4].name == "RZ"
+ assert res[5].name == "SX"
+ assert res[6].name == "RZ"
+ assert res[7].name == "SX"
+ assert res[8].name == "RZ"
+ assert res[9].name == "CNOT"
+ assert res[10].name == "SX"
+ assert res[11].name == "SX"
+
+ mats = []
+ for i in reversed(res):
+ if i.wires == Wires([1]):
+ mats.append(np.kron(np.eye(2), i.matrix))
+ elif i.wires == Wires([0]):
+ mats.append(np.kron(i.matrix, np.eye(2)))
+ elif i.wires == Wires([1, 0]) and i.name == "CNOT":
+ mats.append(np.array([[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0]]))
+ else:
+ mats.append(i.matrix)
+
+ decomposed_matrix = np.linalg.multi_dot(mats)
+
+ assert np.allclose(decomposed_matrix, op.matrix, atol=tol, rtol=0)
+
def test_isingxx_decomposition(self, tol):
"""Tests that the decomposition of the IsingXX gate is correct"""
param = 0.1234
@@ -1536,6 +1589,14 @@ def test_iswap_eigenval(self):
res = op.eigvals
assert np.allclose(res, exp)
+ @pytest.mark.parametrize("siswap_op", [qml.SISWAP, qml.SQISW])
+ def test_siswap_eigenval(self, siswap_op):
+ """Tests that the ISWAP eigenvalue matches the numpy eigenvalues of the ISWAP matrix"""
+ op = siswap_op(wires=[0, 1])
+ exp = np.linalg.eigvals(op.matrix)
+ res = op.eigvals
+ assert np.allclose(res, exp)
+
def test_swap_decomposition(self):
"""Tests the swap operator produces the correct output"""
opr = qml.SWAP(wires=[0, 1])