stimcirq 1.15.dev1733303566__tar.gz → 1.15.dev1734387727__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/PKG-INFO +1 -1
- {stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/setup.py +1 -1
- {stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/stimcirq/__init__.py +1 -1
- {stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/stimcirq/_cirq_to_stim.py +80 -54
- {stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/stimcirq/_cirq_to_stim_test.py +31 -13
- {stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/stimcirq/_cx_swap_gate.py +2 -2
- {stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/stimcirq/_cz_swap_gate.py +2 -2
- {stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/stimcirq/_det_annotation.py +3 -1
- {stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/stimcirq/_measure_and_or_reset_gate.py +3 -3
- {stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/stimcirq/_obs_annotation.py +3 -1
- {stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/stimcirq/_shift_coords_annotation.py +2 -2
- {stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/stimcirq/_stim_to_cirq.py +58 -14
- {stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/stimcirq/_stim_to_cirq_test.py +69 -0
- {stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/stimcirq.egg-info/PKG-INFO +1 -1
- {stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/README.md +0 -0
- {stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/setup.cfg +0 -0
- {stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/stimcirq/_cx_swap_test.py +0 -0
- {stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/stimcirq/_cz_swap_test.py +0 -0
- {stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/stimcirq/_det_annotation_test.py +0 -0
- {stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/stimcirq/_measure_and_or_reset_gate_test.py +0 -0
- {stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/stimcirq/_obs_annotation_test.py +0 -0
- {stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/stimcirq/_shift_coords_annotation_test.py +0 -0
- {stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/stimcirq/_stim_sampler.py +0 -0
- {stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/stimcirq/_stim_sampler_test.py +0 -0
- {stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/stimcirq/_sweep_pauli.py +0 -0
- {stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/stimcirq/_sweep_pauli_test.py +0 -0
- {stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/stimcirq/_two_qubit_asymmetric_depolarize.py +0 -0
- {stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/stimcirq/_two_qubit_asymmetric_depolarize_test.py +0 -0
- {stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/stimcirq.egg-info/SOURCES.txt +0 -0
- {stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/stimcirq.egg-info/dependency_links.txt +0 -0
- {stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/stimcirq.egg-info/requires.txt +0 -0
- {stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/stimcirq.egg-info/top_level.txt +0 -0
|
@@ -7,8 +7,18 @@ import cirq
|
|
|
7
7
|
import stim
|
|
8
8
|
|
|
9
9
|
|
|
10
|
+
def _forward_single_str_tag(op: cirq.CircuitOperation) -> str:
|
|
11
|
+
tags = [tag for tag in op.tags if isinstance(tag, str)]
|
|
12
|
+
if len(tags) == 1:
|
|
13
|
+
return tags[0]
|
|
14
|
+
return ""
|
|
15
|
+
|
|
16
|
+
|
|
10
17
|
def cirq_circuit_to_stim_circuit(
|
|
11
|
-
circuit: cirq.AbstractCircuit,
|
|
18
|
+
circuit: cirq.AbstractCircuit,
|
|
19
|
+
*,
|
|
20
|
+
qubit_to_index_dict: Optional[Dict[cirq.Qid, int]] = None,
|
|
21
|
+
tag_func: Callable[[cirq.Operation], str] = _forward_single_str_tag,
|
|
12
22
|
) -> stim.Circuit:
|
|
13
23
|
"""Converts a cirq circuit into an equivalent stim circuit.
|
|
14
24
|
|
|
@@ -33,6 +43,10 @@ def cirq_circuit_to_stim_circuit(
|
|
|
33
43
|
circuit: The circuit to convert.
|
|
34
44
|
qubit_to_index_dict: Optional. Which integer each qubit should get mapped to. If not specified, defaults to
|
|
35
45
|
indexing qubits in the circuit in sorted order.
|
|
46
|
+
tag_func: Controls the tag attached to the stim instructions the cirq operation turns
|
|
47
|
+
into. If not specified, defaults to checking for string tags on the circuit operation
|
|
48
|
+
and if there is exactly one string tag then using that tag (otherwise not specifying a
|
|
49
|
+
tag).
|
|
36
50
|
|
|
37
51
|
Returns:
|
|
38
52
|
The converted circuit.
|
|
@@ -85,21 +99,29 @@ def cirq_circuit_to_stim_circuit(
|
|
|
85
99
|
# The indices of qubits the gate is operating on.
|
|
86
100
|
targets: List[int],
|
|
87
101
|
|
|
102
|
+
# A custom string associated with the operation, which can be tagged
|
|
103
|
+
# onto any operations appended to the stim circuit.
|
|
104
|
+
tag: str,
|
|
105
|
+
|
|
88
106
|
# Forward compatibility with future arguments.
|
|
89
107
|
**kwargs):
|
|
90
108
|
|
|
91
109
|
edit_circuit.append_operation("H", targets)
|
|
92
110
|
"""
|
|
93
|
-
return cirq_circuit_to_stim_data(circuit, q2i=qubit_to_index_dict, flatten=False)[0]
|
|
111
|
+
return cirq_circuit_to_stim_data(circuit, q2i=qubit_to_index_dict, flatten=False, tag_func=tag_func)[0]
|
|
94
112
|
|
|
95
113
|
|
|
96
114
|
def cirq_circuit_to_stim_data(
|
|
97
|
-
circuit: cirq.AbstractCircuit,
|
|
115
|
+
circuit: cirq.AbstractCircuit,
|
|
116
|
+
*,
|
|
117
|
+
q2i: Optional[Dict[cirq.Qid, int]] = None,
|
|
118
|
+
flatten: bool = False,
|
|
119
|
+
tag_func: Callable[[cirq.Operation], str] = _forward_single_str_tag,
|
|
98
120
|
) -> Tuple[stim.Circuit, List[Tuple[str, int]]]:
|
|
99
121
|
"""Converts a Cirq circuit into a Stim circuit and also metadata about where measurements go."""
|
|
100
122
|
if q2i is None:
|
|
101
123
|
q2i = {q: i for i, q in enumerate(sorted(circuit.all_qubits()))}
|
|
102
|
-
helper = CirqToStimHelper()
|
|
124
|
+
helper = CirqToStimHelper(tag_func=tag_func)
|
|
103
125
|
helper.q2i = q2i
|
|
104
126
|
helper.flatten = flatten
|
|
105
127
|
|
|
@@ -115,11 +137,11 @@ def cirq_circuit_to_stim_data(
|
|
|
115
137
|
return helper.out, helper.key_out
|
|
116
138
|
|
|
117
139
|
|
|
118
|
-
StimTypeHandler = Callable[[stim.Circuit, cirq.Gate, List[int]], None]
|
|
140
|
+
StimTypeHandler = Callable[[stim.Circuit, cirq.Gate, List[int], str], None]
|
|
119
141
|
|
|
120
142
|
|
|
121
143
|
@functools.lru_cache(maxsize=1)
|
|
122
|
-
def gate_to_stim_append_func() -> Dict[cirq.Gate, Callable[[stim.Circuit, List[int]], None]]:
|
|
144
|
+
def gate_to_stim_append_func() -> Dict[cirq.Gate, Callable[[stim.Circuit, List[int], str], None]]:
|
|
123
145
|
"""A dictionary mapping specific gate instances to stim circuit appending functions."""
|
|
124
146
|
x = (cirq.X, False)
|
|
125
147
|
y = (cirq.Y, False)
|
|
@@ -128,29 +150,29 @@ def gate_to_stim_append_func() -> Dict[cirq.Gate, Callable[[stim.Circuit, List[i
|
|
|
128
150
|
ny = (cirq.Y, True)
|
|
129
151
|
nz = (cirq.Z, True)
|
|
130
152
|
|
|
131
|
-
def do_nothing(
|
|
153
|
+
def do_nothing(_gates, _targets, tag):
|
|
132
154
|
pass
|
|
133
155
|
|
|
134
156
|
def use(
|
|
135
157
|
*gates: str, individuals: Sequence[Tuple[str, int]] = ()
|
|
136
|
-
) -> Callable[[stim.Circuit, List[int]], None]:
|
|
158
|
+
) -> Callable[[stim.Circuit, List[int], str], None]:
|
|
137
159
|
if len(gates) == 1 and not individuals:
|
|
138
160
|
(g,) = gates
|
|
139
|
-
return lambda c, t: c.
|
|
161
|
+
return lambda c, t, tag: c.append(g, t, tag=tag)
|
|
140
162
|
|
|
141
163
|
if not individuals:
|
|
142
164
|
|
|
143
|
-
def do(c, t):
|
|
165
|
+
def do(c, t, tag: str):
|
|
144
166
|
for g in gates:
|
|
145
|
-
c.
|
|
167
|
+
c.append(g, t, tag=tag)
|
|
146
168
|
|
|
147
169
|
else:
|
|
148
170
|
|
|
149
|
-
def do(c, t):
|
|
171
|
+
def do(c, t, tag: str):
|
|
150
172
|
for g in gates:
|
|
151
|
-
c.
|
|
173
|
+
c.append(g, t, tag=tag)
|
|
152
174
|
for g, k in individuals:
|
|
153
|
-
c.
|
|
175
|
+
c.append(g, [t[k]], tag=tag)
|
|
154
176
|
|
|
155
177
|
return do
|
|
156
178
|
|
|
@@ -238,14 +260,14 @@ def gate_type_to_stim_append_func() -> Dict[Type[cirq.Gate], StimTypeHandler]:
|
|
|
238
260
|
cirq.AsymmetricDepolarizingChannel: cast(
|
|
239
261
|
StimTypeHandler, _stim_append_asymmetric_depolarizing_channel
|
|
240
262
|
),
|
|
241
|
-
cirq.BitFlipChannel: lambda c, g, t: c.
|
|
242
|
-
"X_ERROR", t, cast(cirq.BitFlipChannel, g).p
|
|
263
|
+
cirq.BitFlipChannel: lambda c, g, t, tag: c.append(
|
|
264
|
+
"X_ERROR", t, cast(cirq.BitFlipChannel, g).p, tag=tag
|
|
243
265
|
),
|
|
244
|
-
cirq.PhaseFlipChannel: lambda c, g, t: c.
|
|
245
|
-
"Z_ERROR", t, cast(cirq.PhaseFlipChannel, g).p
|
|
266
|
+
cirq.PhaseFlipChannel: lambda c, g, t, tag: c.append(
|
|
267
|
+
"Z_ERROR", t, cast(cirq.PhaseFlipChannel, g).p, tag=tag
|
|
246
268
|
),
|
|
247
|
-
cirq.PhaseDampingChannel: lambda c, g, t: c.
|
|
248
|
-
"Z_ERROR", t, 0.5 - math.sqrt(1 - cast(cirq.PhaseDampingChannel, g).gamma) / 2
|
|
269
|
+
cirq.PhaseDampingChannel: lambda c, g, t, tag: c.append(
|
|
270
|
+
"Z_ERROR", t, 0.5 - math.sqrt(1 - cast(cirq.PhaseDampingChannel, g).gamma) / 2, tag=tag
|
|
249
271
|
),
|
|
250
272
|
cirq.RandomGateChannel: cast(StimTypeHandler, _stim_append_random_gate_channel),
|
|
251
273
|
cirq.DepolarizingChannel: cast(StimTypeHandler, _stim_append_depolarizing_channel),
|
|
@@ -253,16 +275,16 @@ def gate_type_to_stim_append_func() -> Dict[Type[cirq.Gate], StimTypeHandler]:
|
|
|
253
275
|
|
|
254
276
|
|
|
255
277
|
def _stim_append_measurement_gate(
|
|
256
|
-
circuit: stim.Circuit, gate: cirq.MeasurementGate, targets: List[int]
|
|
278
|
+
circuit: stim.Circuit, gate: cirq.MeasurementGate, targets: List[int], tag: str
|
|
257
279
|
):
|
|
258
280
|
for i, b in enumerate(gate.invert_mask):
|
|
259
281
|
if b:
|
|
260
282
|
targets[i] = stim.target_inv(targets[i])
|
|
261
|
-
circuit.
|
|
283
|
+
circuit.append("M", targets, tag=tag)
|
|
262
284
|
|
|
263
285
|
|
|
264
286
|
def _stim_append_pauli_measurement_gate(
|
|
265
|
-
circuit: stim.Circuit, gate: cirq.PauliMeasurementGate, targets: List[int]
|
|
287
|
+
circuit: stim.Circuit, gate: cirq.PauliMeasurementGate, targets: List[int], tag: str
|
|
266
288
|
):
|
|
267
289
|
obs: cirq.DensePauliString = gate.observable()
|
|
268
290
|
|
|
@@ -287,11 +309,11 @@ def _stim_append_pauli_measurement_gate(
|
|
|
287
309
|
if obs.coefficient != 1 and obs.coefficient != -1:
|
|
288
310
|
raise NotImplementedError(f"obs.coefficient={obs.coefficient!r} not in [1, -1]")
|
|
289
311
|
|
|
290
|
-
circuit.
|
|
312
|
+
circuit.append("MPP", new_targets, tag=tag)
|
|
291
313
|
|
|
292
314
|
|
|
293
315
|
def _stim_append_spp_gate(
|
|
294
|
-
circuit: stim.Circuit, gate: cirq.PauliStringPhasorGate, targets: List[int]
|
|
316
|
+
circuit: stim.Circuit, gate: cirq.PauliStringPhasorGate, targets: List[int], tag: str
|
|
295
317
|
):
|
|
296
318
|
obs: cirq.DensePauliString = gate.dense_pauli_string
|
|
297
319
|
a = gate.exponent_neg
|
|
@@ -312,26 +334,26 @@ def _stim_append_spp_gate(
|
|
|
312
334
|
return False
|
|
313
335
|
new_targets.pop()
|
|
314
336
|
|
|
315
|
-
circuit.
|
|
337
|
+
circuit.append("SPP" if d == 0.5 else "SPP_DAG", new_targets, tag=tag)
|
|
316
338
|
return True
|
|
317
339
|
|
|
318
340
|
|
|
319
341
|
def _stim_append_dense_pauli_string_gate(
|
|
320
|
-
c: stim.Circuit, g: cirq.BaseDensePauliString, t: List[int]
|
|
342
|
+
c: stim.Circuit, g: cirq.BaseDensePauliString, t: List[int], tag: str
|
|
321
343
|
):
|
|
322
344
|
gates = [None, "X", "Y", "Z"]
|
|
323
345
|
for p, k in zip(g.pauli_mask, t):
|
|
324
346
|
if p:
|
|
325
|
-
c.
|
|
347
|
+
c.append(gates[p], [k], tag=tag)
|
|
326
348
|
|
|
327
349
|
|
|
328
350
|
def _stim_append_asymmetric_depolarizing_channel(
|
|
329
|
-
c: stim.Circuit, g: cirq.AsymmetricDepolarizingChannel, t: List[int]
|
|
351
|
+
c: stim.Circuit, g: cirq.AsymmetricDepolarizingChannel, t: List[int], tag: str
|
|
330
352
|
):
|
|
331
353
|
if cirq.num_qubits(g) == 1:
|
|
332
|
-
c.
|
|
354
|
+
c.append("PAULI_CHANNEL_1", t, [g.p_x, g.p_y, g.p_z], tag=tag)
|
|
333
355
|
elif cirq.num_qubits(g) == 2:
|
|
334
|
-
c.
|
|
356
|
+
c.append(
|
|
335
357
|
"PAULI_CHANNEL_2",
|
|
336
358
|
t,
|
|
337
359
|
[
|
|
@@ -350,33 +372,34 @@ def _stim_append_asymmetric_depolarizing_channel(
|
|
|
350
372
|
g.error_probabilities.get('ZX', 0),
|
|
351
373
|
g.error_probabilities.get('ZY', 0),
|
|
352
374
|
g.error_probabilities.get('ZZ', 0),
|
|
353
|
-
]
|
|
375
|
+
],
|
|
376
|
+
tag=tag,
|
|
354
377
|
)
|
|
355
378
|
else:
|
|
356
379
|
raise NotImplementedError(f'cirq-to-stim gate {g!r}')
|
|
357
380
|
|
|
358
381
|
|
|
359
|
-
def _stim_append_depolarizing_channel(c: stim.Circuit, g: cirq.DepolarizingChannel, t: List[int]):
|
|
382
|
+
def _stim_append_depolarizing_channel(c: stim.Circuit, g: cirq.DepolarizingChannel, t: List[int], tag: str):
|
|
360
383
|
if g.num_qubits() == 1:
|
|
361
|
-
c.
|
|
384
|
+
c.append("DEPOLARIZE1", t, g.p, tag=tag)
|
|
362
385
|
elif g.num_qubits() == 2:
|
|
363
|
-
c.
|
|
386
|
+
c.append("DEPOLARIZE2", t, g.p, tag=tag)
|
|
364
387
|
else:
|
|
365
388
|
raise TypeError(f"Don't know how to turn {g!r} into Stim operations.")
|
|
366
389
|
|
|
367
390
|
|
|
368
|
-
def _stim_append_controlled_gate(c: stim.Circuit, g: cirq.ControlledGate, t: List[int]):
|
|
391
|
+
def _stim_append_controlled_gate(c: stim.Circuit, g: cirq.ControlledGate, t: List[int], tag: str):
|
|
369
392
|
if isinstance(g.sub_gate, cirq.BaseDensePauliString) and g.num_controls() == 1:
|
|
370
393
|
gates = [None, "CX", "CY", "CZ"]
|
|
371
394
|
for p, k in zip(g.sub_gate.pauli_mask, t[1:]):
|
|
372
395
|
if p:
|
|
373
|
-
c.
|
|
396
|
+
c.append(gates[p], [t[0], k], tag=tag)
|
|
374
397
|
if g.sub_gate.coefficient == 1j:
|
|
375
|
-
c.
|
|
398
|
+
c.append("S", t[:1], tag=tag)
|
|
376
399
|
elif g.sub_gate.coefficient == -1:
|
|
377
|
-
c.
|
|
400
|
+
c.append("Z", t[:1], tag=tag)
|
|
378
401
|
elif g.sub_gate.coefficient == -1j:
|
|
379
|
-
c.
|
|
402
|
+
c.append("S_DAG", t[:1], tag=tag)
|
|
380
403
|
elif g.sub_gate.coefficient == 1:
|
|
381
404
|
pass
|
|
382
405
|
else:
|
|
@@ -386,13 +409,13 @@ def _stim_append_controlled_gate(c: stim.Circuit, g: cirq.ControlledGate, t: Lis
|
|
|
386
409
|
raise TypeError(f"Don't know how to turn controlled gate {g!r} into Stim operations.")
|
|
387
410
|
|
|
388
411
|
|
|
389
|
-
def _stim_append_random_gate_channel(c: stim.Circuit, g: cirq.RandomGateChannel, t: List[int]):
|
|
412
|
+
def _stim_append_random_gate_channel(c: stim.Circuit, g: cirq.RandomGateChannel, t: List[int], tag: str):
|
|
390
413
|
if g.sub_gate in [cirq.X, cirq.Y, cirq.Z]:
|
|
391
|
-
c.
|
|
414
|
+
c.append(f"{g.sub_gate}_ERROR", t, g.probability, tag=tag)
|
|
392
415
|
elif isinstance(g.sub_gate, cirq.DensePauliString):
|
|
393
416
|
target_p = [None, stim.target_x, stim.target_y, stim.target_z]
|
|
394
417
|
pauli_targets = [target_p[p](t) for t, p in zip(t, g.sub_gate.pauli_mask) if p]
|
|
395
|
-
c.
|
|
418
|
+
c.append(f"CORRELATED_ERROR", pauli_targets, g.probability, tag=tag)
|
|
396
419
|
else:
|
|
397
420
|
raise NotImplementedError(
|
|
398
421
|
f"Don't know how to turn probabilistic {g!r} into Stim operations."
|
|
@@ -400,33 +423,35 @@ def _stim_append_random_gate_channel(c: stim.Circuit, g: cirq.RandomGateChannel,
|
|
|
400
423
|
|
|
401
424
|
|
|
402
425
|
class CirqToStimHelper:
|
|
403
|
-
def __init__(self):
|
|
426
|
+
def __init__(self, tag_func: Callable[[cirq.Operation], str]):
|
|
404
427
|
self.key_out: List[Tuple[str, int]] = []
|
|
405
428
|
self.out = stim.Circuit()
|
|
406
429
|
self.q2i = {}
|
|
407
430
|
self.have_seen_loop = False
|
|
408
431
|
self.flatten = False
|
|
432
|
+
self.tag_func = tag_func
|
|
409
433
|
|
|
410
|
-
def process_circuit_operation_into_repeat_block(self, op: cirq.CircuitOperation) -> None:
|
|
434
|
+
def process_circuit_operation_into_repeat_block(self, op: cirq.CircuitOperation, tag: str) -> None:
|
|
411
435
|
if self.flatten or op.repetitions == 1:
|
|
412
436
|
moments = cirq.unroll_circuit_op(cirq.Circuit(op), deep=False, tags_to_check=None).moments
|
|
413
437
|
self.process_moments(moments)
|
|
414
438
|
self.out = self.out[:-1] # Remove a trailing TICK (to avoid double TICK)
|
|
415
439
|
return
|
|
416
440
|
|
|
417
|
-
child = CirqToStimHelper()
|
|
441
|
+
child = CirqToStimHelper(tag_func=self.tag_func)
|
|
418
442
|
child.key_out = self.key_out
|
|
419
443
|
child.q2i = self.q2i
|
|
420
444
|
child.have_seen_loop = True
|
|
421
445
|
self.have_seen_loop = True
|
|
422
446
|
child.process_moments(op.transform_qubits(lambda q: op.qubit_map.get(q, q)).circuit)
|
|
423
|
-
self.out
|
|
447
|
+
self.out.append(stim.CircuitRepeatBlock(op.repetitions, child.out, tag=tag))
|
|
424
448
|
|
|
425
449
|
def process_operations(self, operations: Iterable[cirq.Operation]) -> None:
|
|
426
450
|
g2f = gate_to_stim_append_func()
|
|
427
451
|
t2f = gate_type_to_stim_append_func()
|
|
428
452
|
for op in operations:
|
|
429
453
|
assert isinstance(op, cirq.Operation)
|
|
454
|
+
tag = self.tag_func(op)
|
|
430
455
|
op = op.untagged
|
|
431
456
|
gate = op.gate
|
|
432
457
|
targets = [self.q2i[q] for q in op.qubits]
|
|
@@ -441,36 +466,37 @@ class CirqToStimHelper:
|
|
|
441
466
|
edit_measurement_key_lengths=self.key_out,
|
|
442
467
|
targets=targets,
|
|
443
468
|
have_seen_loop=self.have_seen_loop,
|
|
469
|
+
tag=tag,
|
|
444
470
|
)
|
|
445
471
|
continue
|
|
446
472
|
|
|
447
473
|
if isinstance(op, cirq.CircuitOperation):
|
|
448
|
-
self.process_circuit_operation_into_repeat_block(op)
|
|
474
|
+
self.process_circuit_operation_into_repeat_block(op, tag=tag)
|
|
449
475
|
continue
|
|
450
476
|
|
|
451
477
|
# Special case measurement, because of its metadata.
|
|
452
478
|
if isinstance(gate, cirq.PauliStringPhasorGate):
|
|
453
|
-
if _stim_append_spp_gate(self.out, gate, targets):
|
|
479
|
+
if _stim_append_spp_gate(self.out, gate, targets, tag=tag):
|
|
454
480
|
continue
|
|
455
481
|
if isinstance(gate, cirq.PauliMeasurementGate):
|
|
456
482
|
self.key_out.append((gate.key, len(targets)))
|
|
457
|
-
_stim_append_pauli_measurement_gate(self.out, gate, targets)
|
|
483
|
+
_stim_append_pauli_measurement_gate(self.out, gate, targets, tag=tag)
|
|
458
484
|
continue
|
|
459
485
|
if isinstance(gate, cirq.MeasurementGate):
|
|
460
486
|
self.key_out.append((gate.key, len(targets)))
|
|
461
|
-
_stim_append_measurement_gate(self.out, gate, targets)
|
|
487
|
+
_stim_append_measurement_gate(self.out, gate, targets, tag=tag)
|
|
462
488
|
continue
|
|
463
489
|
|
|
464
490
|
# Look for recognized gate values like cirq.H.
|
|
465
491
|
val_append_func = g2f.get(gate)
|
|
466
492
|
if val_append_func is not None:
|
|
467
|
-
val_append_func(self.out, targets)
|
|
493
|
+
val_append_func(self.out, targets, tag=tag)
|
|
468
494
|
continue
|
|
469
495
|
|
|
470
496
|
# Look for recognized gate types like cirq.DepolarizingChannel.
|
|
471
497
|
type_append_func = t2f.get(type(gate))
|
|
472
498
|
if type_append_func is not None:
|
|
473
|
-
type_append_func(self.out, gate, targets)
|
|
499
|
+
type_append_func(self.out, gate, targets, tag=tag)
|
|
474
500
|
continue
|
|
475
501
|
|
|
476
502
|
# Ask unrecognized operations to decompose themselves into simpler operations.
|
|
@@ -489,7 +515,7 @@ class CirqToStimHelper:
|
|
|
489
515
|
|
|
490
516
|
# Append a TICK, unless it was already handled by an internal REPEAT block.
|
|
491
517
|
if length_before == len(self.out) or not isinstance(self.out[-1], stim.CircuitRepeatBlock):
|
|
492
|
-
self.out.
|
|
518
|
+
self.out.append("TICK", [])
|
|
493
519
|
|
|
494
520
|
def process_moments(self, moments: Iterable[cirq.Moment]):
|
|
495
521
|
for moment in moments:
|
|
@@ -74,21 +74,21 @@ def assert_unitary_gate_converts_correctly(gate: cirq.Gate):
|
|
|
74
74
|
# If the gate is translated correctly, the measurement will always be zero.
|
|
75
75
|
|
|
76
76
|
c = stim.Circuit()
|
|
77
|
-
c.
|
|
77
|
+
c.append("H", range(n))
|
|
78
78
|
for i in range(n):
|
|
79
|
-
c.
|
|
80
|
-
c.
|
|
79
|
+
c.append("CNOT", [i, i + n])
|
|
80
|
+
c.append("H", [2 * n])
|
|
81
81
|
for q, p in pre.items():
|
|
82
|
-
c.
|
|
82
|
+
c.append(f"C{p}", [2 * n, q.x])
|
|
83
83
|
qs = cirq.LineQubit.range(n)
|
|
84
84
|
conv_gate, _ = cirq_circuit_to_stim_data(cirq.Circuit(gate(*qs)), q2i={q: q.x for q in qs})
|
|
85
85
|
c += conv_gate
|
|
86
86
|
for q, p in post.items():
|
|
87
|
-
c.
|
|
87
|
+
c.append(f"C{p}", [2 * n, q.x])
|
|
88
88
|
if post.coefficient == -1:
|
|
89
|
-
c.
|
|
90
|
-
c.
|
|
91
|
-
c.
|
|
89
|
+
c.append("Z", [2 * n])
|
|
90
|
+
c.append("H", [2 * n])
|
|
91
|
+
c.append("M", [2 * n])
|
|
92
92
|
correct = np.count_nonzero(c.compile_sampler().sample_bit_packed(10)) == 0
|
|
93
93
|
assert correct, f"{gate!r} failed to turn {pre} into {post}.\nConverted to:\n{conv_gate}\n"
|
|
94
94
|
|
|
@@ -254,13 +254,13 @@ def test_cirq_circuit_to_stim_circuit_custom_stim_method():
|
|
|
254
254
|
**kwargs,
|
|
255
255
|
):
|
|
256
256
|
edit_measurement_key_lengths.append(("custom", 2))
|
|
257
|
-
edit_circuit.
|
|
258
|
-
edit_circuit.
|
|
259
|
-
edit_circuit.
|
|
257
|
+
edit_circuit.append("M", [stim.target_inv(targets[0])])
|
|
258
|
+
edit_circuit.append("M", [targets[0]])
|
|
259
|
+
edit_circuit.append("DETECTOR", [stim.target_rec(-1)])
|
|
260
260
|
|
|
261
261
|
class SecondLastMeasurementWasDeterministicOperation(cirq.Operation):
|
|
262
|
-
def _stim_conversion_(self, edit_circuit: stim.Circuit, **kwargs):
|
|
263
|
-
edit_circuit.
|
|
262
|
+
def _stim_conversion_(self, edit_circuit: stim.Circuit, tag: str, **kwargs):
|
|
263
|
+
edit_circuit.append("DETECTOR", [stim.target_rec(-2)], tag=tag)
|
|
264
264
|
|
|
265
265
|
def with_qubits(self, *new_qubits):
|
|
266
266
|
raise NotImplementedError()
|
|
@@ -392,3 +392,21 @@ def test_random_gate_channel():
|
|
|
392
392
|
E(0.25) X1
|
|
393
393
|
TICK
|
|
394
394
|
""")
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def test_custom_tagging():
|
|
398
|
+
assert stimcirq.cirq_circuit_to_stim_circuit(
|
|
399
|
+
cirq.Circuit(
|
|
400
|
+
cirq.X(cirq.LineQubit(0)).with_tags('test'),
|
|
401
|
+
cirq.X(cirq.LineQubit(0)).with_tags((2, 3, 4)),
|
|
402
|
+
cirq.H(cirq.LineQubit(0)).with_tags('a', 'b'),
|
|
403
|
+
),
|
|
404
|
+
tag_func=lambda op: "PAIR" if len(op.tags) == 2 else repr(op.tags),
|
|
405
|
+
) == stim.Circuit("""
|
|
406
|
+
X[('test',)] 0
|
|
407
|
+
TICK
|
|
408
|
+
X[((2, 3, 4),)] 0
|
|
409
|
+
TICK
|
|
410
|
+
H[PAIR] 0
|
|
411
|
+
TICK
|
|
412
|
+
""")
|
|
@@ -35,8 +35,8 @@ class CXSwapGate(cirq.Gate):
|
|
|
35
35
|
yield cirq.CNOT(a, b)
|
|
36
36
|
yield cirq.SWAP(a, b)
|
|
37
37
|
|
|
38
|
-
def _stim_conversion_(self, edit_circuit: stim.Circuit, targets: List[int], **kwargs):
|
|
39
|
-
edit_circuit.
|
|
38
|
+
def _stim_conversion_(self, edit_circuit: stim.Circuit, targets: List[int], tag: str, **kwargs):
|
|
39
|
+
edit_circuit.append('SWAPCX' if self.inverted else 'CXSWAP', targets, tag=tag)
|
|
40
40
|
|
|
41
41
|
def __pow__(self, power: int) -> 'CXSwapGate':
|
|
42
42
|
if power == +1:
|
|
@@ -22,8 +22,8 @@ class CZSwapGate(cirq.Gate):
|
|
|
22
22
|
yield cirq.SWAP(a, b)
|
|
23
23
|
yield cirq.CZ(a, b)
|
|
24
24
|
|
|
25
|
-
def _stim_conversion_(self, edit_circuit: stim.Circuit, targets: List[int], **kwargs):
|
|
26
|
-
edit_circuit.
|
|
25
|
+
def _stim_conversion_(self, edit_circuit: stim.Circuit, targets: List[int], tag: str, **kwargs):
|
|
26
|
+
edit_circuit.append('CZSWAP', targets, tag=tag)
|
|
27
27
|
|
|
28
28
|
def __pow__(self, power: int) -> 'CZSwapGate':
|
|
29
29
|
if power == +1:
|
|
@@ -77,8 +77,10 @@ class DetAnnotation(cirq.Operation):
|
|
|
77
77
|
|
|
78
78
|
def _stim_conversion_(
|
|
79
79
|
self,
|
|
80
|
+
*,
|
|
80
81
|
edit_circuit: stim.Circuit,
|
|
81
82
|
edit_measurement_key_lengths: List[Tuple[str, int]],
|
|
83
|
+
tag: str,
|
|
82
84
|
have_seen_loop: bool = False,
|
|
83
85
|
**kwargs,
|
|
84
86
|
):
|
|
@@ -111,4 +113,4 @@ class DetAnnotation(cirq.Operation):
|
|
|
111
113
|
f" in an earlier moment (or earlier in the same moment's operation order)."
|
|
112
114
|
)
|
|
113
115
|
|
|
114
|
-
edit_circuit.
|
|
116
|
+
edit_circuit.append("DETECTOR", rec_targets, self.coordinate_metadata, tag=tag)
|
{stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/stimcirq/_measure_and_or_reset_gate.py
RENAMED
|
@@ -91,15 +91,15 @@ class MeasureAndOrResetGate(cirq.Gate):
|
|
|
91
91
|
result += self.basis
|
|
92
92
|
return result
|
|
93
93
|
|
|
94
|
-
def _stim_conversion_(self, edit_circuit: stim.Circuit, targets: List[int], **kwargs):
|
|
94
|
+
def _stim_conversion_(self, *, edit_circuit: stim.Circuit, targets: List[int], tag: str, **kwargs):
|
|
95
95
|
if self.invert_measure:
|
|
96
96
|
targets[0] = stim.target_inv(targets[0])
|
|
97
97
|
if self.measure_flip_probability:
|
|
98
98
|
edit_circuit.append_operation(
|
|
99
|
-
self._stim_op_name(), targets, self.measure_flip_probability
|
|
99
|
+
self._stim_op_name(), targets, self.measure_flip_probability, tag=tag
|
|
100
100
|
)
|
|
101
101
|
else:
|
|
102
|
-
edit_circuit.append_operation(self._stim_op_name(), targets)
|
|
102
|
+
edit_circuit.append_operation(self._stim_op_name(), targets, tag=tag)
|
|
103
103
|
|
|
104
104
|
def __str__(self) -> str:
|
|
105
105
|
result = self._stim_op_name()
|
|
@@ -75,9 +75,11 @@ class CumulativeObservableAnnotation(cirq.Operation):
|
|
|
75
75
|
|
|
76
76
|
def _stim_conversion_(
|
|
77
77
|
self,
|
|
78
|
+
*,
|
|
78
79
|
edit_circuit: stim.Circuit,
|
|
79
80
|
edit_measurement_key_lengths: List[Tuple[str, int]],
|
|
80
81
|
have_seen_loop: bool = False,
|
|
82
|
+
tag: str,
|
|
81
83
|
**kwargs,
|
|
82
84
|
):
|
|
83
85
|
# Ideally these references would all be resolved ahead of time, to avoid the redundant
|
|
@@ -109,4 +111,4 @@ class CumulativeObservableAnnotation(cirq.Operation):
|
|
|
109
111
|
f" in an earlier moment (or earlier in the same moment's operation order)."
|
|
110
112
|
)
|
|
111
113
|
|
|
112
|
-
edit_circuit.
|
|
114
|
+
edit_circuit.append("OBSERVABLE_INCLUDE", rec_targets, self.observable_index, tag=tag)
|
{stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/stimcirq/_shift_coords_annotation.py
RENAMED
|
@@ -49,5 +49,5 @@ class ShiftCoordsAnnotation(cirq.Operation):
|
|
|
49
49
|
def _is_comment_(self) -> bool:
|
|
50
50
|
return True
|
|
51
51
|
|
|
52
|
-
def _stim_conversion_(self, edit_circuit: stim.Circuit, **kwargs):
|
|
53
|
-
edit_circuit.append_operation("SHIFT_COORDS", [], self.shift)
|
|
52
|
+
def _stim_conversion_(self, *, edit_circuit: stim.Circuit, tag: str, **kwargs):
|
|
53
|
+
edit_circuit.append_operation("SHIFT_COORDS", [], self.shift, tag=tag)
|
|
@@ -84,8 +84,12 @@ class CircuitTranslationTracker:
|
|
|
84
84
|
m = cirq.num_qubits(gate)
|
|
85
85
|
if not all(t.is_qubit_target for t in targets) or len(targets) % m != 0:
|
|
86
86
|
raise NotImplementedError(f"instruction={instruction!r}")
|
|
87
|
+
if instruction.tag:
|
|
88
|
+
tags = [instruction.tag]
|
|
89
|
+
else:
|
|
90
|
+
tags = ()
|
|
87
91
|
for k in range(0, len(targets), m):
|
|
88
|
-
self.append_operation(gate(*[cirq.LineQubit(t.value) for t in targets[k : k + m]]))
|
|
92
|
+
self.append_operation(gate(*[cirq.LineQubit(t.value) for t in targets[k : k + m]]).with_tags(*tags))
|
|
89
93
|
|
|
90
94
|
def process_tick(self, instruction: stim.CircuitInstruction) -> None:
|
|
91
95
|
self.full_circuit += self.tick_circuit or cirq.Moment()
|
|
@@ -128,7 +132,7 @@ class CircuitTranslationTracker:
|
|
|
128
132
|
self.process_gate_instruction(gate, instruction)
|
|
129
133
|
|
|
130
134
|
def process_repeat_block(self, block: stim.CircuitRepeatBlock):
|
|
131
|
-
if self.flatten or block.repeat_count == 1:
|
|
135
|
+
if self.flatten or (block.repeat_count == 1 and block.tag == ""):
|
|
132
136
|
self.process_circuit(block.repeat_count, block.body_copy())
|
|
133
137
|
return
|
|
134
138
|
|
|
@@ -141,6 +145,10 @@ class CircuitTranslationTracker:
|
|
|
141
145
|
child.process_circuit(1, block.body_copy())
|
|
142
146
|
|
|
143
147
|
# Circuit operation will always be in their own cirq.Moment
|
|
148
|
+
if block.tag == "":
|
|
149
|
+
tags = ()
|
|
150
|
+
else:
|
|
151
|
+
tags = (block.tag,)
|
|
144
152
|
if len(self.tick_circuit):
|
|
145
153
|
self.full_circuit += self.tick_circuit
|
|
146
154
|
self.full_circuit += cirq.Moment(
|
|
@@ -148,7 +156,7 @@ class CircuitTranslationTracker:
|
|
|
148
156
|
cirq.FrozenCircuit(child.full_circuit + child.tick_circuit),
|
|
149
157
|
repetitions=block.repeat_count,
|
|
150
158
|
use_repetition_ids=False,
|
|
151
|
-
)
|
|
159
|
+
).with_tags(*tags)
|
|
152
160
|
)
|
|
153
161
|
self.tick_circuit = cirq.Circuit()
|
|
154
162
|
|
|
@@ -168,6 +176,10 @@ class CircuitTranslationTracker:
|
|
|
168
176
|
flip_probability = args[0]
|
|
169
177
|
|
|
170
178
|
targets: List[stim.GateTarget] = instruction.targets_copy()
|
|
179
|
+
if instruction.tag:
|
|
180
|
+
tags = [instruction.tag]
|
|
181
|
+
else:
|
|
182
|
+
tags = ()
|
|
171
183
|
for t in targets:
|
|
172
184
|
if not t.is_qubit_target:
|
|
173
185
|
raise NotImplementedError(f"instruction={instruction!r}")
|
|
@@ -180,7 +192,7 @@ class CircuitTranslationTracker:
|
|
|
180
192
|
invert_measure=t.is_inverted_result_target,
|
|
181
193
|
key=key,
|
|
182
194
|
measure_flip_probability=flip_probability,
|
|
183
|
-
).resolve(cirq.LineQubit(t.value))
|
|
195
|
+
).resolve(cirq.LineQubit(t.value)).with_tags(*tags)
|
|
184
196
|
)
|
|
185
197
|
|
|
186
198
|
def process_circuit(self, repetitions: int, circuit: stim.Circuit) -> None:
|
|
@@ -219,6 +231,10 @@ class CircuitTranslationTracker:
|
|
|
219
231
|
raise NotImplementedError("Noisy MPP")
|
|
220
232
|
|
|
221
233
|
targets: List[stim.GateTarget] = instruction.targets_copy()
|
|
234
|
+
if instruction.tag:
|
|
235
|
+
tags = [instruction.tag]
|
|
236
|
+
else:
|
|
237
|
+
tags = ()
|
|
222
238
|
start = 0
|
|
223
239
|
while start < len(targets):
|
|
224
240
|
next_start = start + 1
|
|
@@ -230,13 +246,17 @@ class CircuitTranslationTracker:
|
|
|
230
246
|
obs = _stim_targets_to_dense_pauli_string(group)
|
|
231
247
|
qubits = [cirq.LineQubit(t.value) for t in group]
|
|
232
248
|
key = str(self.get_next_measure_id())
|
|
233
|
-
self.append_operation(cirq.PauliMeasurementGate(obs, key=key).on(*qubits))
|
|
249
|
+
self.append_operation(cirq.PauliMeasurementGate(obs, key=key).on(*qubits).with_tags(*tags))
|
|
234
250
|
|
|
235
251
|
def process_spp_dag(self, instruction: stim.CircuitInstruction) -> None:
|
|
236
252
|
self.process_spp(instruction, dag=True)
|
|
237
253
|
|
|
238
254
|
def process_spp(self, instruction: stim.CircuitInstruction, dag: bool = False) -> None:
|
|
239
255
|
targets: List[stim.GateTarget] = instruction.targets_copy()
|
|
256
|
+
if instruction.tag:
|
|
257
|
+
tags = [instruction.tag]
|
|
258
|
+
else:
|
|
259
|
+
tags = ()
|
|
240
260
|
start = 0
|
|
241
261
|
while start < len(targets):
|
|
242
262
|
next_start = start + 1
|
|
@@ -250,13 +270,17 @@ class CircuitTranslationTracker:
|
|
|
250
270
|
self.append_operation(cirq.PauliStringPhasorGate(
|
|
251
271
|
obs,
|
|
252
272
|
exponent_neg=-0.5 if dag else 0.5,
|
|
253
|
-
).on(*qubits))
|
|
273
|
+
).on(*qubits).with_tags(*tags))
|
|
254
274
|
|
|
255
275
|
def process_m_pair(self, instruction: stim.CircuitInstruction, basis: str) -> None:
|
|
256
276
|
args = instruction.gate_args_copy()
|
|
257
277
|
if args and args[0]:
|
|
258
278
|
raise NotImplementedError("Noisy M" + basis*2)
|
|
259
279
|
|
|
280
|
+
if instruction.tag:
|
|
281
|
+
tags = [instruction.tag]
|
|
282
|
+
else:
|
|
283
|
+
tags = ()
|
|
260
284
|
targets: List[stim.GateTarget] = instruction.targets_copy()
|
|
261
285
|
for k in range(0, len(targets), 2):
|
|
262
286
|
obs = cirq.DensePauliString(basis * 2)
|
|
@@ -264,7 +288,7 @@ class CircuitTranslationTracker:
|
|
|
264
288
|
obs *= -1
|
|
265
289
|
qubits = [cirq.LineQubit(targets[0].value), cirq.LineQubit(targets[1].value)]
|
|
266
290
|
key = str(self.get_next_measure_id())
|
|
267
|
-
self.append_operation(cirq.PauliMeasurementGate(obs, key=key).on(*qubits))
|
|
291
|
+
self.append_operation(cirq.PauliMeasurementGate(obs, key=key).on(*qubits).with_tags(*tags))
|
|
268
292
|
|
|
269
293
|
def process_mxx(self, instruction: stim.CircuitInstruction) -> None:
|
|
270
294
|
self.process_m_pair(instruction, "X")
|
|
@@ -286,12 +310,16 @@ class CircuitTranslationTracker:
|
|
|
286
310
|
self.append_operation(cirq.PauliMeasurementGate(obs, key=key).on(*qubits))
|
|
287
311
|
|
|
288
312
|
def process_correlated_error(self, instruction: stim.CircuitInstruction) -> None:
|
|
313
|
+
if instruction.tag:
|
|
314
|
+
tags = [instruction.tag]
|
|
315
|
+
else:
|
|
316
|
+
tags = ()
|
|
289
317
|
args = instruction.gate_args_copy()
|
|
290
318
|
probability = args[0] if args else 0
|
|
291
319
|
targets = instruction.targets_copy()
|
|
292
320
|
qubits = [cirq.LineQubit(t.value) for t in targets]
|
|
293
321
|
self.append_operation(
|
|
294
|
-
_stim_targets_to_dense_pauli_string(targets).on(*qubits).with_probability(probability)
|
|
322
|
+
_stim_targets_to_dense_pauli_string(targets).on(*qubits).with_probability(probability).with_tags(*tags)
|
|
295
323
|
)
|
|
296
324
|
|
|
297
325
|
def coords_after_offset(
|
|
@@ -316,20 +344,28 @@ class CircuitTranslationTracker:
|
|
|
316
344
|
return [str(self.num_measurements_seen + t.value) for t in targets], []
|
|
317
345
|
|
|
318
346
|
def process_detector(self, instruction: stim.CircuitInstruction) -> None:
|
|
347
|
+
if instruction.tag:
|
|
348
|
+
tags = [instruction.tag]
|
|
349
|
+
else:
|
|
350
|
+
tags = ()
|
|
319
351
|
coords = self.coords_after_offset(instruction.gate_args_copy())
|
|
320
352
|
keys, rels = self.resolve_measurement_record_keys(instruction.targets_copy())
|
|
321
353
|
self.append_operation(
|
|
322
|
-
DetAnnotation(parity_keys=keys, relative_keys=rels, coordinate_metadata=coords)
|
|
354
|
+
DetAnnotation(parity_keys=keys, relative_keys=rels, coordinate_metadata=coords).with_tags(*tags)
|
|
323
355
|
)
|
|
324
356
|
|
|
325
357
|
def process_observable_include(self, instruction: stim.CircuitInstruction) -> None:
|
|
358
|
+
if instruction.tag:
|
|
359
|
+
tags = [instruction.tag]
|
|
360
|
+
else:
|
|
361
|
+
tags = ()
|
|
326
362
|
args = instruction.gate_args_copy()
|
|
327
363
|
index = 0 if not args else int(args[0])
|
|
328
364
|
keys, rels = self.resolve_measurement_record_keys(instruction.targets_copy())
|
|
329
365
|
self.append_operation(
|
|
330
366
|
CumulativeObservableAnnotation(
|
|
331
367
|
parity_keys=keys, relative_keys=rels, observable_index=index
|
|
332
|
-
)
|
|
368
|
+
).with_tags(*tags)
|
|
333
369
|
)
|
|
334
370
|
|
|
335
371
|
def process_qubit_coords(self, instruction: stim.CircuitInstruction) -> None:
|
|
@@ -341,9 +377,13 @@ class CircuitTranslationTracker:
|
|
|
341
377
|
self.qubit_coords[t.value] = cirq.GridQubit(*coords)
|
|
342
378
|
|
|
343
379
|
def process_shift_coords(self, instruction: stim.CircuitInstruction) -> None:
|
|
380
|
+
if instruction.tag:
|
|
381
|
+
tags = [instruction.tag]
|
|
382
|
+
else:
|
|
383
|
+
tags = ()
|
|
344
384
|
args = instruction.gate_args_copy()
|
|
345
385
|
if not self.flatten:
|
|
346
|
-
self.append_operation(ShiftCoordsAnnotation(args))
|
|
386
|
+
self.append_operation(ShiftCoordsAnnotation(args).with_tags(*tags))
|
|
347
387
|
for k, a in enumerate(args):
|
|
348
388
|
self.origin[k] += a
|
|
349
389
|
|
|
@@ -364,6 +404,10 @@ class CircuitTranslationTracker:
|
|
|
364
404
|
def __call__(
|
|
365
405
|
self, tracker: 'CircuitTranslationTracker', instruction: stim.CircuitInstruction
|
|
366
406
|
) -> None:
|
|
407
|
+
if instruction.tag:
|
|
408
|
+
tags = [instruction.tag]
|
|
409
|
+
else:
|
|
410
|
+
tags = ()
|
|
367
411
|
targets: List[stim.GateTarget] = instruction.targets_copy()
|
|
368
412
|
for k in range(0, len(targets), 2):
|
|
369
413
|
a = targets[k]
|
|
@@ -379,13 +423,13 @@ class CircuitTranslationTracker:
|
|
|
379
423
|
stim_sweep_bit_index=a.value,
|
|
380
424
|
cirq_sweep_symbol=f'sweep[{a.value}]',
|
|
381
425
|
pauli=self.pauli_gate,
|
|
382
|
-
).on(cirq.LineQubit(b.value))
|
|
426
|
+
).on(cirq.LineQubit(b.value)).with_tags(*tags)
|
|
383
427
|
)
|
|
384
428
|
else:
|
|
385
429
|
if not a.is_qubit_target or not b.is_qubit_target:
|
|
386
430
|
raise NotImplementedError(f"instruction={instruction!r}")
|
|
387
431
|
tracker.append_operation(
|
|
388
|
-
self.gate(cirq.LineQubit(a.value), cirq.LineQubit(b.value))
|
|
432
|
+
self.gate(cirq.LineQubit(a.value), cirq.LineQubit(b.value)).with_tags(*tags)
|
|
389
433
|
)
|
|
390
434
|
|
|
391
435
|
class OneToOneMeasurementHandler:
|
|
@@ -422,7 +466,7 @@ class CircuitTranslationTracker:
|
|
|
422
466
|
noise = CircuitTranslationTracker.OneToOneNoisyGateHandler
|
|
423
467
|
sweep_gate = CircuitTranslationTracker.SweepableGateHandler
|
|
424
468
|
|
|
425
|
-
def not_impl(message) -> Callable[[Any], None]:
|
|
469
|
+
def not_impl(message) -> Callable[[Any, Any], None]:
|
|
426
470
|
def handler(
|
|
427
471
|
tracker: CircuitTranslationTracker, instruction: stim.CircuitInstruction
|
|
428
472
|
) -> None:
|
|
@@ -682,3 +682,72 @@ def test_stim_circuit_to_cirq_circuit_spp():
|
|
|
682
682
|
SPP Z0
|
|
683
683
|
TICK
|
|
684
684
|
""")
|
|
685
|
+
|
|
686
|
+
|
|
687
|
+
def test_tags_convert():
|
|
688
|
+
assert stimcirq.stim_circuit_to_cirq_circuit(stim.Circuit("""
|
|
689
|
+
H[my_tag] 0
|
|
690
|
+
""")) == cirq.Circuit(
|
|
691
|
+
cirq.H(cirq.LineQubit(0)).with_tags('my_tag'),
|
|
692
|
+
)
|
|
693
|
+
|
|
694
|
+
|
|
695
|
+
@pytest.mark.parametrize('gate', sorted(stim.gate_data().keys()))
|
|
696
|
+
def test_every_operation_converts_tags(gate: str):
|
|
697
|
+
if gate in [
|
|
698
|
+
"ELSE_CORRELATED_ERROR",
|
|
699
|
+
"HERALDED_ERASE",
|
|
700
|
+
"HERALDED_PAULI_CHANNEL_1",
|
|
701
|
+
"TICK",
|
|
702
|
+
"REPEAT",
|
|
703
|
+
"MPAD",
|
|
704
|
+
"QUBIT_COORDS",
|
|
705
|
+
]:
|
|
706
|
+
pytest.skip()
|
|
707
|
+
|
|
708
|
+
data = stim.gate_data(gate)
|
|
709
|
+
stim_circuit = stim.Circuit()
|
|
710
|
+
arg = None
|
|
711
|
+
targets = [0, 1]
|
|
712
|
+
if data.num_parens_arguments_range.start:
|
|
713
|
+
arg = [2**-6] * data.num_parens_arguments_range.start
|
|
714
|
+
if data.takes_pauli_targets:
|
|
715
|
+
targets = [stim.target_x(0), stim.target_y(1)]
|
|
716
|
+
if data.takes_measurement_record_targets and not data.is_unitary:
|
|
717
|
+
stim_circuit.append("M", [0], tag='custom_tag')
|
|
718
|
+
targets = [stim.target_rec(-1)]
|
|
719
|
+
if gate == 'SHIFT_COORDS':
|
|
720
|
+
targets = []
|
|
721
|
+
if gate == 'OBSERVABLE_INCLUDE':
|
|
722
|
+
arg = [1]
|
|
723
|
+
stim_circuit.append(gate, targets, arg, tag='custom_tag')
|
|
724
|
+
cirq_circuit = stimcirq.stim_circuit_to_cirq_circuit(stim_circuit)
|
|
725
|
+
assert any(cirq_circuit.all_operations())
|
|
726
|
+
for op in cirq_circuit.all_operations():
|
|
727
|
+
assert op.tags == ('custom_tag',)
|
|
728
|
+
restored_circuit = stimcirq.cirq_circuit_to_stim_circuit(cirq_circuit)
|
|
729
|
+
assert restored_circuit.pop() == stim.CircuitInstruction("TICK")
|
|
730
|
+
assert all(instruction.tag == 'custom_tag' for instruction in restored_circuit)
|
|
731
|
+
if gate not in ['MXX', 'MYY', 'MZZ']:
|
|
732
|
+
assert restored_circuit == stim_circuit
|
|
733
|
+
|
|
734
|
+
|
|
735
|
+
def test_loop_tagging():
|
|
736
|
+
stim_circuit = stim.Circuit("""
|
|
737
|
+
REPEAT[custom-tag] 5 {
|
|
738
|
+
H[tag2] 0
|
|
739
|
+
TICK
|
|
740
|
+
}
|
|
741
|
+
""")
|
|
742
|
+
cirq_circuit = stimcirq.stim_circuit_to_cirq_circuit(stim_circuit)
|
|
743
|
+
assert cirq_circuit == cirq.Circuit(
|
|
744
|
+
cirq.CircuitOperation(
|
|
745
|
+
cirq.FrozenCircuit(
|
|
746
|
+
cirq.H(cirq.LineQubit(0)).with_tags('tag2'),
|
|
747
|
+
),
|
|
748
|
+
repetitions=5,
|
|
749
|
+
use_repetition_ids=False,
|
|
750
|
+
).with_tags('custom-tag')
|
|
751
|
+
)
|
|
752
|
+
restored_circuit = stimcirq.cirq_circuit_to_stim_circuit(cirq_circuit)
|
|
753
|
+
assert restored_circuit == stim_circuit
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/stimcirq/_det_annotation_test.py
RENAMED
|
File without changes
|
|
File without changes
|
{stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/stimcirq/_obs_annotation_test.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{stimcirq-1.15.dev1733303566 → stimcirq-1.15.dev1734387727}/stimcirq.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|