Skip to content

Commit 9008717

Browse files
authored
Merge pull request #409 from QuTech-Delft/CQT-294-Fix-McKay-decomposer-bug-with-decomposing-Cycling-Cliffords
[CQT-294] Fix mc kay decomposer bug with decomposing cycling cliffords
2 parents ac65d23 + 6af057e commit 9008717

File tree

2 files changed

+109
-10
lines changed

2 files changed

+109
-10
lines changed

opensquirrel/passes/decomposer/mckay_decomposer.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
from math import atan2, cos, pi, sin, sqrt
44

5-
from opensquirrel import X90, Rz
5+
from opensquirrel import X90, I, Rz
66
from opensquirrel.common import ATOL, normalize_angle
7-
from opensquirrel.ir import BlochSphereRotation, Gate
7+
from opensquirrel.ir import Axis, BlochSphereRotation, Gate
88
from opensquirrel.passes.decomposer import ZXZDecomposer
99
from opensquirrel.passes.decomposer.general_decomposer import Decomposer
1010

@@ -25,20 +25,26 @@ def decompose(self, g: Gate) -> list[Gate]:
2525
return [g]
2626

2727
if abs(g.angle) < ATOL:
28-
return []
28+
return [I(g.qubit)]
2929

3030
if g.axis[0] == 0 and g.axis[1] == 0:
3131
rz_angle = float(g.angle * g.axis[2])
3232
return [Rz(g.qubit, rz_angle)]
3333

3434
zxz_decomposition = ZXZDecomposer().decompose(g)
3535
zxz_angle = 0.0
36-
if len(zxz_decomposition) >= 2 and isinstance(zxz_decomposition[1], BlochSphereRotation):
37-
zxz_angle = zxz_decomposition[1].angle
36+
if len(zxz_decomposition) >= 2:
37+
zxz_angle = next(
38+
gate.angle
39+
for gate in zxz_decomposition
40+
if isinstance(gate, BlochSphereRotation) and gate.axis == Axis(1, 0, 0)
41+
)
3842

3943
if abs(zxz_angle - pi / 2) < ATOL:
40-
zxz_decomposition[1] = X90(g.qubit)
41-
return zxz_decomposition
44+
return [
45+
X90(g.qubit) if isinstance(gate, BlochSphereRotation) and gate.axis == Axis(1, 0, 0) else gate
46+
for gate in zxz_decomposition
47+
]
4248

4349
# McKay decomposition
4450
za_mod = sqrt(cos(g.angle / 2) ** 2 + (g.axis[2] * sin(g.angle / 2)) ** 2)

test/decomposer/test_mckay_decomposer.py

Lines changed: 96 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import numpy as np
66
import pytest
77

8-
from opensquirrel import CNOT, CR, X90, H, I, Rz, X, Y, Z
8+
from opensquirrel import CNOT, CR, X90, Y90, H, I, Rz, S, Sdag, X, Y, Z, mX90, mY90
99
from opensquirrel.ir import BlochSphereRotation, Gate
1010
from opensquirrel.passes.decomposer import McKayDecomposer
1111
from opensquirrel.passes.decomposer.general_decomposer import check_gate_replacement
@@ -25,10 +25,11 @@ def test_ignores_2q_gates(decomposer: McKayDecomposer, gate: Gate, expected_resu
2525
assert decomposer.decompose(gate) == expected_result
2626

2727

28-
def test_identity_empty_decomposition(decomposer: McKayDecomposer) -> None:
28+
def test_identity_decomposition(decomposer: McKayDecomposer) -> None:
2929
gate = I(0)
3030
decomposed_gate = decomposer.decompose(gate)
31-
assert decomposed_gate == []
31+
expected_result = [I(0)]
32+
assert decomposed_gate == expected_result
3233

3334

3435
def test_x(decomposer: McKayDecomposer) -> None:
@@ -81,3 +82,95 @@ def test_all_octants_of_bloch_sphere_rotation(decomposer: McKayDecomposer) -> No
8182
arbitrary_operation = BlochSphereRotation(qubit=0, axis=axis, angle=angle, phase=phase)
8283
decomposed_arbitrary_operation = decomposer.decompose(arbitrary_operation)
8384
check_gate_replacement(arbitrary_operation, decomposed_arbitrary_operation)
85+
86+
87+
@pytest.mark.parametrize(
88+
("gate", "expected_result"),
89+
[
90+
(I(0), [I(0)]),
91+
(X(0), [X90(0), X90(0)]),
92+
(Y(0), [Rz(0, math.pi), X90(0), X90(0)]),
93+
(Z(0), [Rz(0, math.pi)]),
94+
(
95+
BlochSphereRotation(
96+
qubit=0, axis=[1 / math.sqrt(3), 1 / math.sqrt(3), 1 / math.sqrt(3)], angle=2 * math.pi / 3, phase=0
97+
),
98+
[X90(0), Rz(0, math.pi / 2)],
99+
),
100+
(
101+
BlochSphereRotation(qubit=0, axis=[1, 1, 1], angle=-2 * math.pi / 3, phase=0),
102+
[X90(0), Rz(0, math.pi / 2), X90(0), Rz(0, math.pi / 2)],
103+
),
104+
(
105+
BlochSphereRotation(qubit=0, axis=[-1, 1, 1], angle=2 * math.pi / 3, phase=0),
106+
[Rz(0, -math.pi / 2), X90(0), Rz(0, math.pi)],
107+
),
108+
(
109+
BlochSphereRotation(qubit=0, axis=[-1, 1, 1], angle=-2 * math.pi / 3, phase=0),
110+
[Rz(0, -math.pi / 2), X90(0), Rz(0, math.pi / 2), X90(0), Rz(0, math.pi)],
111+
),
112+
(BlochSphereRotation(qubit=0, axis=[1, -1, 1], angle=2 * math.pi / 3, phase=0), [Rz(0, math.pi / 2), X90(0)]),
113+
(
114+
BlochSphereRotation(qubit=0, axis=[1, -1, 1], angle=-2 * math.pi / 3, phase=0),
115+
[Rz(0, math.pi / 2), X90(0), Rz(0, math.pi / 2), X90(0)],
116+
),
117+
(BlochSphereRotation(qubit=0, axis=[1, 1, -1], angle=2 * math.pi / 3, phase=0), [Rz(0, -math.pi / 2), X90(0)]),
118+
(
119+
BlochSphereRotation(
120+
qubit=0, axis=[1 / math.sqrt(3), 1 / math.sqrt(3), -1 / math.sqrt(3)], angle=-2 * math.pi / 3, phase=0
121+
),
122+
[Rz(0, math.pi / 2), X90(0), Rz(0, math.pi / 2), X90(0), Rz(0, math.pi)],
123+
),
124+
(X90(0), [X90(0)]),
125+
(mX90(0), [Rz(0, math.pi / 2), X90(0), Rz(0, math.pi / 2), X90(0), Rz(0, math.pi / 2)]),
126+
(Y90(0), [Rz(0, -math.pi / 2), X90(0), Rz(0, math.pi / 2)]),
127+
(mY90(0), [X90(0), Rz(0, math.pi / 2), X90(0), Rz(0, math.pi)]),
128+
(S(0), [Rz(0, math.pi / 2)]),
129+
(Sdag(0), [Rz(0, -math.pi / 2)]),
130+
(H(0), [Rz(0, math.pi / 2), X90(0), Rz(0, math.pi / 2)]),
131+
(
132+
BlochSphereRotation(qubit=0, axis=[1, 1, 0], angle=math.pi, phase=math.pi / 2),
133+
[Rz(0, -3 * math.pi / 4), X90(0), X90(0), Rz(0, -math.pi / 4)],
134+
),
135+
(BlochSphereRotation(qubit=0, axis=[0, 1, 1], angle=math.pi, phase=math.pi / 2), [X90(0), Rz(0, math.pi)]),
136+
(
137+
BlochSphereRotation(qubit=0, axis=[-1, 1, 0], angle=math.pi, phase=math.pi / 2),
138+
[Rz(0, 3 * math.pi / 4), X90(0), X90(0), Rz(0, math.pi / 4)],
139+
),
140+
(
141+
BlochSphereRotation(qubit=0, axis=[1, 0, -1], angle=math.pi, phase=math.pi / 2),
142+
[Rz(0, -math.pi / 2), X90(0), Rz(0, -math.pi / 2)],
143+
),
144+
(BlochSphereRotation(qubit=0, axis=[0, -1, 1], angle=math.pi, phase=math.pi / 2), [Rz(0, math.pi), X90(0)]),
145+
],
146+
ids=[
147+
"I",
148+
"X",
149+
"Y",
150+
"Z",
151+
"C1",
152+
"C2",
153+
"C3",
154+
"C4",
155+
"C5",
156+
"C6",
157+
"C7",
158+
"C8",
159+
"X90",
160+
"mX90",
161+
"Y90",
162+
"mY90",
163+
"Z90",
164+
"mZ90",
165+
"H1",
166+
"H2",
167+
"H3",
168+
"H4",
169+
"H5",
170+
"H6",
171+
],
172+
)
173+
def test_single_qubit_clifford_gates(decomposer: McKayDecomposer, gate: Gate, expected_result: list[Gate]) -> None:
174+
decomposed_gates = decomposer.decompose(gate)
175+
check_gate_replacement(gate, decomposed_gates)
176+
assert decomposed_gates == expected_result

0 commit comments

Comments
 (0)