Skip to content

Commit 24d83c5

Browse files
checkpoint
1 parent b4293e7 commit 24d83c5

3 files changed

Lines changed: 97 additions & 24 deletions

File tree

qualtran/rotation_synthesis/channels/channel.py

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -132,19 +132,28 @@ def to_cirq(self, fmt: str = "xz", qs: Optional[Sequence[cirq.Qid]] = None) -> c
132132
q = cirq.q(0)
133133
return cirq.Circuit(ctr.to_cirq(self.to_matrix(), fmt, q))
134134

135-
def to_quirk(self, fmt: str = "xz") -> str:
135+
def to_quirk(self, fmt: str = "xz", allow_global_phase: bool = False) -> str:
136136
"""Retruns a quirk link representing the channel operation.
137137
138138
Args:
139139
fmt: The gates to use (see the documentation of to_sequence).
140+
allow_global_phase: whether the result can have a global phase or not.
141+
If this is set then each gate (except H) gets replaced by SU2 version:
142+
- S -> Rz(pi/2)
143+
- T -> Rz(pi/4)
144+
- X -> Rx(-pi)
145+
- Y -> Ry(-pi)
146+
- Z -> Rz(-pi)
147+
Then a prefix composed of SXSX that corrects the phase difference caused by H gates
148+
is added.
140149
Returns:
141150
A quirk link.
142151
Raises:
143152
ValueError: If twirl=True
144153
"""
145154
if self.twirl:
146155
raise ValueError("to_quirk is not supported when twirl=True")
147-
gates = ctr.to_quirk(self.to_matrix(), fmt)
156+
gates = ctr.to_quirk(self.to_matrix(), fmt, allow_global_phase)
148157
cols = '[' + ','.join(f'[{g}]' for g in gates) + ']'
149158
return "https://algassert.com/quirk#circuit={\"cols\":%s}" % cols
150159

@@ -277,30 +286,43 @@ def to_cirq(self, fmt: str = "xz", qs: Optional[Sequence[cirq.Qid]] = None) -> c
277286
),
278287
)
279288

280-
def to_quirk(self, fmt: str = "xz") -> str:
289+
def to_quirk(self, fmt: str = "xz", allow_global_phase: bool = False) -> str:
281290
"""Retruns a quirk link representing the channel operation.
282291
283292
Args:
284293
fmt: The gates to use (see the documentation of to_sequence).
294+
allow_global_phase: whether the result can have a global phase or not.
295+
If this is set then each gate (except H) gets replaced by SU2 version:
296+
- S -> Rz(pi/2)
297+
- T -> Rz(pi/4)
298+
- X -> Rx(-pi)
299+
- Y -> Ry(-pi)
300+
- Z -> Rz(-pi)
301+
Then a prefix composed of SXSX that corrects the phase difference caused by H gates
302+
is added.
285303
Returns:
286304
A quirk link.
287305
"""
288306
correction = self.correction
289307
if not isinstance(correction, UnitaryChannel):
290308
raise ValueError(f"to_quirk is not supported for correction of type {type(correction)}")
291-
rot = ctr.to_quirk(self.rotation.to_matrix(), fmt)
292-
cor = ctr.to_quirk(correction.to_matrix(), fmt)
309+
rot = ctr.to_quirk(self.rotation.to_matrix(), fmt, allow_global_phase)
310+
cor = ctr.to_quirk(correction.to_matrix(), fmt, allow_global_phase)
311+
if allow_global_phase:
312+
xgate = '"X"'
313+
else:
314+
xgate = '{"id":"Rzft","arg":"-pi"}'
293315
first_row = []
294316
second_row = []
295317
# CNOT
296318
first_row.append("\"\"")
297-
second_row.append("\"X\"")
319+
second_row.append(xgate)
298320
# rotation
299321
first_row.extend(rot)
300322
second_row.extend("1" for _ in rot)
301323
# CNOT
302324
first_row.append("\"\"")
303-
second_row.append("\"X\"")
325+
second_row.append(xgate)
304326
# measure
305327
first_row.append("1")
306328
second_row.append("\"Measure\"")

qualtran/rotation_synthesis/matrix/clifford_t_repr.py

Lines changed: 51 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -52,26 +52,31 @@ def _xyz_sequence(matrix: su2_ct.SU2CliffordT) -> tuple[str, ...]:
5252
]
5353

5454

55-
def _xz_sequence(matrix: su2_ct.SU2CliffordT, use_hs: bool = True) -> Optional[tuple[str, ...]]:
55+
def _xz_sequence(matrix: su2_ct.SU2CliffordT, use_hs: bool = True, prv: str = 'dummy') -> Optional[tuple[str, ...]]:
56+
# if use_hs:
57+
# matrix = matrix.reduce()
5658
if matrix.det() == 2:
5759
return clifford(matrix)
5860
cliffords = [su2_ct.ISqrt2]
5961
if use_hs:
6062
cliffords.append(su2_ct.HSqrt2)
6163
cliffords.append(su2_ct.HSqrt2 @ su2_ct.SSqrt2)
6264
candidates = []
65+
pref = prv.removesuffix('*')
6366
for name, t in _T_list:
67+
if name.startswith(pref): continue
6468
for c in cliffords:
6569
new = c.adjoint() @ matrix
6670
new = t.adjoint() @ new
6771
new = new.scale_down()
6872
if new is None or not new.is_valid():
6973
continue
70-
seq = _xz_sequence(new, False)
74+
seq = _xz_sequence(new, False, name)
7175
if seq is None:
7276
continue
7377
gates = cast(tuple[str, ...], c.gates)
7478
candidates.append(seq + (name,) + gates)
79+
break
7580
if not candidates:
7681
return None
7782
return min(candidates, key=len)
@@ -113,19 +118,34 @@ def to_sequence(matrix: su2_ct.SU2CliffordT, fmt: str = 'xyz') -> tuple[str, ...
113118
}
114119

115120

116-
def _to_quirk_name(name: str) -> str:
117-
if name == "I":
118-
return "1"
119-
if name in ("X", "Y", "Z", "H"):
120-
return "\"" + name + "\""
121-
if name == "S":
122-
return "\"Z^½\""
123-
if name == "S*":
124-
return "\"Z^-½\""
125-
if name.startswith("T"):
126-
if name.endswith("*"):
127-
return "\"" + name[1].upper() + "^-¼" + "\""
128-
return "\"" + name[1].upper() + "^¼" + "\""
121+
def _to_quirk_name(name: str, allow_global_phase: bool = False) -> str:
122+
if allow_global_phase:
123+
if name == "I":
124+
return "1"
125+
if name in ("X", "Y", "Z", "H"):
126+
return "\"" + name + "\""
127+
if name == "S":
128+
return "\"Z^½\""
129+
if name == "S*":
130+
return "\"Z^-½\""
131+
if name.startswith("T"):
132+
if name.endswith("*"):
133+
return "\"" + name[1].upper() + "^-¼" + "\""
134+
return "\"" + name[1].upper() + "^¼" + "\""
135+
else:
136+
if name == "I":
137+
return "1"
138+
if name == "H":
139+
return "\"H\""
140+
if name in ("X", "Y", "Z"):
141+
return '{"id":"R%sft","arg":"-pi"}'%(name.lower())
142+
if name == "S":
143+
return '{"id":"Rzft","arg":"pi/2"}'
144+
if name == "S*":
145+
return '{"id":"Rzft","arg":"-pi/2"}'
146+
if name.startswith("T"):
147+
angle = ['pi/4', '-pi/4'][name.endswith('*')]
148+
return '{"id":"R%sft","arg":"%s"}'%(name[1].lower(), angle)
129149
raise ValueError(f"{name=} is not supported")
130150

131151

@@ -145,13 +165,27 @@ def to_cirq(
145165
return tuple(_CIRQ_GATE_MAP[g](q) for g in to_sequence(matrix, fmt))
146166

147167

148-
def to_quirk(matrix: su2_ct.SU2CliffordT, fmt: str) -> tuple[str, ...]:
168+
def to_quirk(matrix: su2_ct.SU2CliffordT, fmt: str, allow_global_phase: bool = False) -> tuple[str, ...]:
149169
"""Retruns a representation of the matrix as a sequence of quirk symbols.
150170
151171
Args:
152172
matrix: The matrix to represent.
153173
fmt: The gates to use (see the documentation of to_sequence).
174+
allow_global_phase: whether the result can have a global phase or not.
175+
If this is set then each gate (except H) gets replaced by SU2 version:
176+
- S -> Rz(pi/2)
177+
- T -> Rz(pi/4)
178+
- X -> Rx(-pi)
179+
- Y -> Ry(-pi)
180+
- Z -> Rz(-pi)
181+
Then a prefix composed of SXSX that corrects the phase difference caused by H gates
182+
is added.
154183
Returns:
155184
A tuple quirk symbols.
156185
"""
157-
return tuple(_to_quirk_name(name) for name in to_sequence(matrix, fmt))
186+
sequence = to_sequence(matrix, fmt)
187+
phase_correction = ()
188+
if not allow_global_phase:
189+
phase = sum(g=='H' for g in sequence) % 4
190+
phase_correction = ('"Z^½"', '"X"', '"Z^½"', '"X"') * phase
191+
return phase_correction + tuple(_to_quirk_name(name, allow_global_phase) for name in sequence)

qualtran/rotation_synthesis/matrix/clifford_t_repr_test.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,28 @@ def test_to_xyz_seq(g):
4141
got = su2_ct.SU2CliffordT.from_sequence(seq)
4242
assert got == g
4343

44+
def reduce(g: su2_ct.SU2CliffordT):
45+
import qualtran.rotation_synthesis.rings.zsqrt2 as z2
46+
import qualtran.rotation_synthesis.rings.zw as zw
47+
if not(g.det() > 2 * z2.LAMBDA_KLIUCHNIKOV):
48+
return g
49+
if not all(v.is_divisible_by(zw.LAMBDA_KLIUCHNIKOV) for v in g.matrix.flat):
50+
return g
51+
return reduce(su2_ct.SU2CliffordT([[v//zw.LAMBDA_KLIUCHNIKOV for v in r] for r in g.matrix]))
4452

4553
@pytest.mark.parametrize("g", _make_random_su(50, 5, random_cliffords=True, seed=0))
4654
def test_to_xz_seq(g):
55+
g = reduce(g)
4756
seq = ctr.to_sequence(g, 'xz')
4857
assert not any('Ty' in g for g in seq)
58+
first_t = None
59+
for i in range(len(seq)):
60+
if seq[i].startswith('T'):
61+
first_t = i
62+
break
63+
if first_t is not None:
64+
ts = 'Tx', 'Tx*', 'Tz', 'Tz*'
65+
assert all(s in ts for s in seq[first_t:-2]), f'{seq=}'
4966
got = su2_ct.SU2CliffordT.from_sequence(seq)
5067
assert got == g
5168

0 commit comments

Comments
 (0)