Skip to content

Commit 3229b05

Browse files
[BugFix] Fix powers of ISWAP and SISWAP (#7361)
**Context:** **Description of the Change:** **Benefits:** **Possible Drawbacks:** **Related GitHub Issues:** Fixes #7359 [sc-90233] --------- Co-authored-by: Yushao Chen (Jerry) <[email protected]>
1 parent b4e77d7 commit 3229b05

File tree

3 files changed

+65
-25
lines changed

3 files changed

+65
-25
lines changed

doc/releases/changelog-dev.md

+2
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,8 @@
290290
* Fixed a bug where `two_qubit_decomposition` provides an incorrect decomposition for some special matrices.
291291
[(#7340)](https://github.com/PennyLaneAI/pennylane/pull/7340)
292292

293+
* Fixes a bug where the powers of `qml.ISWAP` and `qml.SISWAP` were decomposed incorrectly.
294+
[(#7361)](https://github.com/PennyLaneAI/pennylane/pull/7361)
293295

294296
<h3>Contributors ✍️</h3>
295297

pennylane/ops/qubit/non_parametric_ops.py

+11-5
Original file line numberDiff line numberDiff line change
@@ -1788,10 +1788,12 @@ def compute_decomposition(wires: WiresLike) -> list[qml.operation.Operator]:
17881788
]
17891789

17901790
def pow(self, z: Union[int, float]) -> list[qml.operation.Operator]:
1791-
z_mod2 = z % 2
1792-
if abs(z_mod2 - 0.5) < 1e-6:
1791+
z_mod4 = z % 4
1792+
if abs(z_mod4 - 0.5) < 1e-6:
17931793
return [SISWAP(wires=self.wires)]
1794-
return super().pow(z_mod2)
1794+
if abs(z_mod4 - 2) < 1e-6:
1795+
return [qml.Z(wires=self.wires[0]), qml.Z(wires=self.wires[1])]
1796+
return super().pow(z_mod4)
17951797

17961798

17971799
def _iswap_decomp_resources():
@@ -1962,8 +1964,12 @@ def compute_decomposition(wires: WiresLike) -> list[qml.operation.Operator]:
19621964
]
19631965

19641966
def pow(self, z: Union[int, float]) -> list[qml.operation.Operator]:
1965-
z_mod4 = z % 4
1966-
return [ISWAP(wires=self.wires)] if z_mod4 == 2 else super().pow(z_mod4)
1967+
z_mod8 = z % 8
1968+
if abs(z_mod8 - 2) < 1e-6:
1969+
return [ISWAP(wires=self.wires)]
1970+
if abs(z_mod8 - 4) < 1e-6:
1971+
return [qml.Z(wires=self.wires[0]), qml.Z(wires=self.wires[1])]
1972+
return super().pow(z_mod8)
19671973

19681974

19691975
def _siswap_decomp_resources():

tests/ops/qubit/test_non_parametric_ops.py

+52-20
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import numpy as np
2222
import pytest
23+
import scipy as sp
2324
from gate_data import (
2425
CCZ,
2526
CH,
@@ -826,7 +827,6 @@ def test_repr(self):
826827
qml.PauliZ(0),
827828
qml.Hadamard("a"),
828829
qml.SWAP(wires=(0, 1)),
829-
qml.ISWAP(wires=(0, 1)),
830830
qml.ECR(wires=(0, 1)),
831831
# Controlled operations
832832
qml.CNOT(wires=(0, 1)),
@@ -918,16 +918,37 @@ def test_pauliz_general_power(self, n):
918918
assert op_pow[0].__class__ is qml.PhaseShift
919919
assert qml.math.allclose(op_pow[0].data[0], np.pi * (n % 2))
920920

921-
@pytest.mark.parametrize("n", (0.5, 2.5, -1.5))
922-
def test_ISWAP_sqaure_root(self, n):
923-
"""Test that SISWAP is the square root of ISWAP."""
924-
op = qml.ISWAP(wires=(0, 1))
925-
926-
assert op.pow(n)[0].__class__ is qml.SISWAP
921+
@pytest.mark.parametrize(
922+
"n, expected",
923+
[
924+
(0, []),
925+
(4, []),
926+
(-4, []),
927+
(-3, [qml.ISWAP(wires=(0, 1))]),
928+
(5, [qml.ISWAP(wires=(0, 1))]),
929+
(0.5, [qml.SISWAP(wires=(0, 1))]),
930+
(4.5, [qml.SISWAP(wires=(0, 1))]),
931+
(2, [qml.Z(0), qml.Z(1)]),
932+
(-2, [qml.Z(0), qml.Z(1)]),
933+
(6, [qml.Z(0), qml.Z(1)]),
934+
],
935+
)
936+
def test_ISWAP_powers(self, n, expected):
937+
"""Check that the special powers of ISWAP are correct."""
927938

928-
sqrt_mat = qml.matrix(op.pow, wire_order=[0, 1])(n)
929-
sqrt_mat_squared = qml.math.linalg.matrix_power(sqrt_mat, 2)
930-
assert qml.math.allclose(sqrt_mat_squared, qml.matrix(op))
939+
op = qml.ISWAP(wires=(0, 1))
940+
op_mat = qml.matrix(op)
941+
pow_ops = op.pow(n)
942+
assert pow_ops == expected
943+
if not pow_ops:
944+
pow_ops.append(qml.I(wires=(0, 1)))
945+
mat = qml.matrix(qml.prod(*pow_ops), wire_order=[0, 1])
946+
expected = (
947+
qml.math.linalg.matrix_power(op_mat, n)
948+
if isinstance(n, int)
949+
else sp.linalg.fractional_matrix_power(op_mat, n)
950+
)
951+
assert qml.math.allclose(mat, expected)
931952

932953
@pytest.mark.parametrize("offset", (0, 4, -4))
933954
def test_S_pow(self, offset):
@@ -971,17 +992,28 @@ def test_SX_pow(self, offset):
971992
with pytest.raises(qml.operation.PowUndefinedError):
972993
op.pow(2.43 + offset)
973994

974-
@pytest.mark.parametrize("offset", (0, 4, -4))
975-
def test_SISWAP_pow(self, offset):
976-
"""Test powers of the SISWAP operator"""
977-
op = qml.SISWAP(wires=("b", "c"))
978-
979-
assert len(op.pow(0 + offset)) == 0
980-
assert op.pow(1 + offset)[0].__class__ is qml.SISWAP
981-
assert op.pow(2 + offset)[0].__class__ is qml.ISWAP
995+
@pytest.mark.parametrize(
996+
"n, expected",
997+
[
998+
(0, []),
999+
(8, []),
1000+
(9, [qml.SISWAP(wires=(0, 1))]),
1001+
(2, [qml.ISWAP(wires=(0, 1))]),
1002+
(4, [qml.Z(0), qml.Z(1)]),
1003+
],
1004+
)
1005+
def test_SISWAP_powers(self, n, expected):
1006+
"""Check that the special powers of SISWAP are correct."""
9821007

983-
with pytest.raises(qml.operation.PowUndefinedError):
984-
op.pow(2.34 + offset)
1008+
op = qml.SISWAP(wires=(0, 1))
1009+
op_mat = qml.matrix(op)
1010+
pow_ops = op.pow(n)
1011+
assert pow_ops == expected
1012+
if not pow_ops:
1013+
pow_ops.append(qml.I(wires=(0, 1)))
1014+
mat = qml.matrix(qml.prod(*pow_ops), wire_order=[0, 1])
1015+
expected = qml.math.linalg.matrix_power(op_mat, n)
1016+
assert qml.math.allclose(mat, expected)
9851017

9861018
@pytest.mark.parametrize("op", (qml.WireCut(0), qml.Barrier(0)))
9871019
@pytest.mark.parametrize("n", (2, 0.123, -2.3))

0 commit comments

Comments
 (0)