cirq-core 1.5.0.dev20250324234903__py3-none-any.whl → 1.5.0.dev20250325011740__py3-none-any.whl
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.
Potentially problematic release.
This version of cirq-core might be problematic. Click here for more details.
- cirq/_version.py +1 -1
- cirq/_version_test.py +1 -1
- cirq/contrib/paulistring/__init__.py +4 -0
- cirq/contrib/paulistring/pauli_string_measurement_with_readout_mitigation.py +379 -0
- cirq/contrib/paulistring/pauli_string_measurement_with_readout_mitigation_test.py +528 -0
- cirq/contrib/shuffle_circuits/shuffle_circuits_with_readout_benchmarking.py +40 -19
- cirq/contrib/shuffle_circuits/shuffle_circuits_with_readout_benchmarking_test.py +133 -74
- {cirq_core-1.5.0.dev20250324234903.dist-info → cirq_core-1.5.0.dev20250325011740.dist-info}/METADATA +1 -1
- {cirq_core-1.5.0.dev20250324234903.dist-info → cirq_core-1.5.0.dev20250325011740.dist-info}/RECORD +12 -10
- {cirq_core-1.5.0.dev20250324234903.dist-info → cirq_core-1.5.0.dev20250325011740.dist-info}/LICENSE +0 -0
- {cirq_core-1.5.0.dev20250324234903.dist-info → cirq_core-1.5.0.dev20250325011740.dist-info}/WHEEL +0 -0
- {cirq_core-1.5.0.dev20250324234903.dist-info → cirq_core-1.5.0.dev20250325011740.dist-info}/top_level.txt +0 -0
cirq/_version.py
CHANGED
cirq/_version_test.py
CHANGED
|
@@ -42,3 +42,7 @@ from cirq.contrib.paulistring.clifford_optimize import (
|
|
|
42
42
|
)
|
|
43
43
|
|
|
44
44
|
from cirq.contrib.paulistring.optimize import optimized_circuit as optimized_circuit
|
|
45
|
+
|
|
46
|
+
from cirq.contrib.paulistring.pauli_string_measurement_with_readout_mitigation import (
|
|
47
|
+
measure_pauli_strings as measure_pauli_strings,
|
|
48
|
+
)
|
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
# Copyright 2025 The Cirq Developers
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
"""Tools for measuring expectation values of Pauli strings with readout error mitigation."""
|
|
15
|
+
import time
|
|
16
|
+
from typing import List, Union, Dict, Optional, Tuple
|
|
17
|
+
import attrs
|
|
18
|
+
|
|
19
|
+
import numpy as np
|
|
20
|
+
|
|
21
|
+
from cirq import ops, circuits, work
|
|
22
|
+
from cirq.contrib.shuffle_circuits import run_shuffled_with_readout_benchmarking
|
|
23
|
+
from cirq.experiments import SingleQubitReadoutCalibrationResult
|
|
24
|
+
from cirq.experiments.readout_confusion_matrix import TensoredConfusionMatrices
|
|
25
|
+
from cirq.study import ResultDict
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@attrs.frozen
|
|
29
|
+
class PauliStringMeasurementResult:
|
|
30
|
+
"""Result of measuring a Pauli string.
|
|
31
|
+
|
|
32
|
+
Attributes:
|
|
33
|
+
pauli_string: The Pauli string that is measured.
|
|
34
|
+
mitigated_expectation: The error-mitigated expectation value of the Pauli string.
|
|
35
|
+
mitigated_stddev: The standard deviation of the error-mitigated expectation value.
|
|
36
|
+
unmitigated_expectation: The unmitigated expectation value of the Pauli string.
|
|
37
|
+
unmitigated_stddev: The standard deviation of the unmitigated expectation value.
|
|
38
|
+
calibration_result: The calibration result for single-qubit readout errors.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
pauli_string: ops.PauliString
|
|
42
|
+
mitigated_expectation: float
|
|
43
|
+
mitigated_stddev: float
|
|
44
|
+
unmitigated_expectation: float
|
|
45
|
+
unmitigated_stddev: float
|
|
46
|
+
calibration_result: Optional[SingleQubitReadoutCalibrationResult] = None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@attrs.frozen
|
|
50
|
+
class CircuitToPauliStringsMeasurementResult:
|
|
51
|
+
"""Result of measuring Pauli strings on a circuit.
|
|
52
|
+
|
|
53
|
+
Attributes:
|
|
54
|
+
circuit: The circuit that is measured.
|
|
55
|
+
results: A list of PauliStringMeasurementResult objects.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
circuit: circuits.FrozenCircuit
|
|
59
|
+
results: List[PauliStringMeasurementResult]
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _validate_input(
|
|
63
|
+
circuits_to_pauli: Dict[circuits.FrozenCircuit, list[ops.PauliString]],
|
|
64
|
+
pauli_repetitions: int,
|
|
65
|
+
readout_repetitions: int,
|
|
66
|
+
num_random_bitstrings: int,
|
|
67
|
+
rng_or_seed: Union[np.random.Generator, int],
|
|
68
|
+
):
|
|
69
|
+
if not circuits_to_pauli:
|
|
70
|
+
raise ValueError("Input circuits must not be empty.")
|
|
71
|
+
|
|
72
|
+
for circuit in circuits_to_pauli.keys():
|
|
73
|
+
if not isinstance(circuit, circuits.FrozenCircuit):
|
|
74
|
+
raise TypeError("All keys in 'circuits_to_pauli' must be FrozenCircuit instances.")
|
|
75
|
+
|
|
76
|
+
for pauli_strings in circuits_to_pauli.values():
|
|
77
|
+
for pauli_str in pauli_strings:
|
|
78
|
+
if not isinstance(pauli_str, ops.PauliString):
|
|
79
|
+
raise TypeError(
|
|
80
|
+
f"All elements in the Pauli string lists must be cirq.PauliString "
|
|
81
|
+
f"instances, got {type(pauli_str)}."
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
if all(q == ops.I for q in pauli_str):
|
|
85
|
+
raise ValueError(
|
|
86
|
+
"Empty Pauli strings or Pauli strings consisting"
|
|
87
|
+
"only of Pauli I are not allowed. Please provide"
|
|
88
|
+
"valid input Pauli strings."
|
|
89
|
+
)
|
|
90
|
+
if pauli_str.coefficient.imag != 0:
|
|
91
|
+
raise ValueError(
|
|
92
|
+
"Cannot compute expectation value of a non-Hermitian PauliString. "
|
|
93
|
+
"Coefficient must be real."
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# Check rng is a numpy random generator
|
|
97
|
+
if not isinstance(rng_or_seed, np.random.Generator) and not isinstance(rng_or_seed, int):
|
|
98
|
+
raise ValueError("Must provide a numpy random generator or a seed")
|
|
99
|
+
|
|
100
|
+
# Check pauli_repetitions is bigger than 0
|
|
101
|
+
if pauli_repetitions <= 0:
|
|
102
|
+
raise ValueError("Must provide non-zero pauli_repetitions.")
|
|
103
|
+
|
|
104
|
+
# Check num_random_bitstrings is bigger than or equal to 0
|
|
105
|
+
if num_random_bitstrings < 0:
|
|
106
|
+
raise ValueError("Must provide zero or more num_random_bitstrings.")
|
|
107
|
+
|
|
108
|
+
# Check readout_repetitions is bigger than 0
|
|
109
|
+
if readout_repetitions <= 0:
|
|
110
|
+
raise ValueError("Must provide non-zero readout_repetitions for readout calibration.")
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _pauli_string_to_basis_change_ops(
|
|
114
|
+
pauli_string: ops.PauliString, qid_list: list[ops.Qid]
|
|
115
|
+
) -> List[ops.Operation]:
|
|
116
|
+
"""Creates operations to change to the eigenbasis of the given Pauli string.
|
|
117
|
+
|
|
118
|
+
This function constructs a list of ops.Operation that performs basis changes
|
|
119
|
+
necessary to measure the given pauli_string in the computational basis.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
pauli_string: The Pauli string to diagonalize.
|
|
123
|
+
qid_list: An ordered list of the qubits in the circuit.
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
A list of Operations that, when applied before measurement in the
|
|
127
|
+
computational basis, effectively measures in the eigenbasis of
|
|
128
|
+
pauli_strings.
|
|
129
|
+
"""
|
|
130
|
+
operations = []
|
|
131
|
+
for qubit in qid_list: # Iterate over ALL qubits in the circuit
|
|
132
|
+
if qubit in pauli_string:
|
|
133
|
+
pauli_op = pauli_string[qubit]
|
|
134
|
+
if pauli_op == ops.X:
|
|
135
|
+
operations.append(ops.ry(-np.pi / 2)(qubit)) # =cirq.H
|
|
136
|
+
elif pauli_op == ops.Y:
|
|
137
|
+
operations.append(ops.rx(np.pi / 2)(qubit))
|
|
138
|
+
# If pauli_op is Z or I, no operation needed
|
|
139
|
+
return operations
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _build_one_qubit_confusion_matrix(e0: float, e1: float) -> np.ndarray:
|
|
143
|
+
"""Builds a 2x2 confusion matrix for a single qubit.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
e0: the 0->1 readout error rate.
|
|
147
|
+
e1: the 1->0 readout error rate.
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
A 2x2 NumPy array representing the confusion matrix.
|
|
151
|
+
"""
|
|
152
|
+
return np.array([[1 - e0, e1], [e0, 1 - e1]])
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _build_many_one_qubits_confusion_matrix(
|
|
156
|
+
qubits_to_error: SingleQubitReadoutCalibrationResult,
|
|
157
|
+
) -> list[np.ndarray]:
|
|
158
|
+
"""Builds a list of confusion matrices from calibration results.
|
|
159
|
+
|
|
160
|
+
This function iterates through the calibration results for each qubit and
|
|
161
|
+
constructs a list of single-qubit confusion matrices.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
qubits_to_error: An object containing calibration results for
|
|
165
|
+
single-qubit readout errors, including zero-state and one-state errors
|
|
166
|
+
for each qubit.
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
A list of NumPy arrays, where each array is a 2x2 confusion matrix
|
|
170
|
+
for a qubit. The order of matrices corresponds to the order of qubits
|
|
171
|
+
in the calibration results (alphabetical order by qubit name).
|
|
172
|
+
"""
|
|
173
|
+
cms: list[np.ndarray] = []
|
|
174
|
+
|
|
175
|
+
for qubit in sorted(qubits_to_error.zero_state_errors.keys()):
|
|
176
|
+
e0 = qubits_to_error.zero_state_errors[qubit]
|
|
177
|
+
e1 = qubits_to_error.one_state_errors[qubit]
|
|
178
|
+
cms.append(_build_one_qubit_confusion_matrix(e0, e1))
|
|
179
|
+
return cms
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def _build_many_one_qubits_empty_confusion_matrix(qubits_length: int) -> list[np.ndarray]:
|
|
183
|
+
"""Builds a list of empty confusion matrices"""
|
|
184
|
+
return [_build_one_qubit_confusion_matrix(0, 0) for _ in range(qubits_length)]
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _process_pauli_measurement_results(
|
|
188
|
+
qubits: List[ops.Qid],
|
|
189
|
+
pauli_strings: List[ops.PauliString],
|
|
190
|
+
circuit_results: List[ResultDict],
|
|
191
|
+
calibration_results: Dict[Tuple[ops.Qid, ...], SingleQubitReadoutCalibrationResult],
|
|
192
|
+
pauli_repetitions: int,
|
|
193
|
+
timestamp: float,
|
|
194
|
+
disable_readout_mitigation: bool = False,
|
|
195
|
+
) -> List[PauliStringMeasurementResult]:
|
|
196
|
+
"""Calculates both error-mitigated expectation values and unmitigated expectation values
|
|
197
|
+
from measurement results.
|
|
198
|
+
|
|
199
|
+
This function takes the results from shuffled readout benchmarking and:
|
|
200
|
+
1. Constructs a tensored confusion matrix for error mitigation.
|
|
201
|
+
2. Mitigates readout errors for each Pauli string measurement.
|
|
202
|
+
3. Calculates and returns both error-mitigated and unmitigated expectation values.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
qubits: Qubits to build confusion matrices for. In a sorted order.
|
|
206
|
+
pauli_strings: The list of PauliStrings that are measured.
|
|
207
|
+
circuit_results: A list of ResultDict obtained
|
|
208
|
+
from running the Pauli measurement circuits.
|
|
209
|
+
confusion_matrices: A list of confusion matrices from calibration results.
|
|
210
|
+
pauli_repetitions: The number of repetitions used for Pauli string measurements.
|
|
211
|
+
timestamp: The timestamp of the calibration results.
|
|
212
|
+
disable_readout_mitigation: If set to True, returns no error-mitigated error
|
|
213
|
+
expectation values.
|
|
214
|
+
|
|
215
|
+
Returns:
|
|
216
|
+
A list of PauliStringMeasurementResult.
|
|
217
|
+
"""
|
|
218
|
+
|
|
219
|
+
pauli_measurement_results: List[PauliStringMeasurementResult] = []
|
|
220
|
+
|
|
221
|
+
for pauli_index, circuit_result in enumerate(circuit_results):
|
|
222
|
+
measurement_results = circuit_result.measurements["m"]
|
|
223
|
+
|
|
224
|
+
pauli_string = pauli_strings[pauli_index]
|
|
225
|
+
qubits_sorted = sorted(pauli_string.qubits)
|
|
226
|
+
qubit_indices = [qubits.index(q) for q in qubits_sorted]
|
|
227
|
+
|
|
228
|
+
confusion_matrices = (
|
|
229
|
+
_build_many_one_qubits_confusion_matrix(calibration_results[tuple(qubits_sorted)])
|
|
230
|
+
if disable_readout_mitigation is False
|
|
231
|
+
else _build_many_one_qubits_empty_confusion_matrix(len(qubits_sorted))
|
|
232
|
+
)
|
|
233
|
+
tensored_cm = TensoredConfusionMatrices(
|
|
234
|
+
confusion_matrices,
|
|
235
|
+
[[q] for q in qubits_sorted],
|
|
236
|
+
repetitions=pauli_repetitions,
|
|
237
|
+
timestamp=timestamp,
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
# Create a mask for the relevant qubits in the measurement results
|
|
241
|
+
relevant_bits = measurement_results[:, qubit_indices]
|
|
242
|
+
|
|
243
|
+
# Calculate the mitigated expectation.
|
|
244
|
+
raw_mitigated_values, raw_d_m = tensored_cm.readout_mitigation_pauli_uncorrelated(
|
|
245
|
+
qubits_sorted, relevant_bits
|
|
246
|
+
)
|
|
247
|
+
mitigated_values_with_coefficient = raw_mitigated_values * pauli_string.coefficient.real
|
|
248
|
+
d_m_with_coefficient = raw_d_m * abs(pauli_string.coefficient.real)
|
|
249
|
+
|
|
250
|
+
# Calculate the unmitigated expectation.
|
|
251
|
+
parity = np.sum(relevant_bits, axis=1) % 2
|
|
252
|
+
raw_unmitigated_values = 1 - 2 * np.mean(parity)
|
|
253
|
+
raw_d_unmit = 2 * np.sqrt(np.mean(parity) * (1 - np.mean(parity)) / pauli_repetitions)
|
|
254
|
+
unmitigated_value_with_coefficient = raw_unmitigated_values * pauli_string.coefficient
|
|
255
|
+
d_unmit_with_coefficient = raw_d_unmit * abs(pauli_string.coefficient)
|
|
256
|
+
|
|
257
|
+
pauli_measurement_results.append(
|
|
258
|
+
PauliStringMeasurementResult(
|
|
259
|
+
pauli_string=pauli_strings[pauli_index],
|
|
260
|
+
mitigated_expectation=mitigated_values_with_coefficient,
|
|
261
|
+
mitigated_stddev=d_m_with_coefficient,
|
|
262
|
+
unmitigated_expectation=unmitigated_value_with_coefficient,
|
|
263
|
+
unmitigated_stddev=d_unmit_with_coefficient,
|
|
264
|
+
calibration_result=(
|
|
265
|
+
calibration_results[tuple(qubits_sorted)]
|
|
266
|
+
if disable_readout_mitigation is False
|
|
267
|
+
else None
|
|
268
|
+
),
|
|
269
|
+
)
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
return pauli_measurement_results
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def measure_pauli_strings(
|
|
276
|
+
circuits_to_pauli: Dict[circuits.FrozenCircuit, list[ops.PauliString]],
|
|
277
|
+
sampler: work.Sampler,
|
|
278
|
+
pauli_repetitions: int,
|
|
279
|
+
readout_repetitions: int,
|
|
280
|
+
num_random_bitstrings: int,
|
|
281
|
+
rng_or_seed: Union[np.random.Generator, int],
|
|
282
|
+
) -> List[CircuitToPauliStringsMeasurementResult]:
|
|
283
|
+
"""Measures expectation values of Pauli strings on given circuits with/without
|
|
284
|
+
readout error mitigation.
|
|
285
|
+
|
|
286
|
+
This function takes a list of circuits and corresponding List[Pauli string] to measure.
|
|
287
|
+
For each circuit-List[Pauli string] pair, it:
|
|
288
|
+
1. Constructs circuits to measure the Pauli string expectation value by
|
|
289
|
+
adding basis change moments and measurement operations.
|
|
290
|
+
2. Runs shuffled readout benchmarking on these circuits to calibrate readout errors.
|
|
291
|
+
3. Mitigates readout errors using the calibrated confusion matrices.
|
|
292
|
+
4. Calculates and returns both error-mitigated and unmitigatedexpectation values for
|
|
293
|
+
each Pauli string.
|
|
294
|
+
|
|
295
|
+
Args:
|
|
296
|
+
circuits_to_pauli: A dictionary mapping circuits to a list of Pauli strings
|
|
297
|
+
to measure.
|
|
298
|
+
sampler: The sampler to use.
|
|
299
|
+
pauli_repetitions: The number of repetitions for each circuit when measuring
|
|
300
|
+
Pauli strings.
|
|
301
|
+
readout_repetitions: The number of repetitions for readout calibration
|
|
302
|
+
in the shuffled benchmarking.
|
|
303
|
+
num_random_bitstrings: The number of random bitstrings to use in shuffled
|
|
304
|
+
benchmarking.
|
|
305
|
+
rng_or_seed: A random number generator or seed for the shuffled benchmarking.
|
|
306
|
+
|
|
307
|
+
Returns:
|
|
308
|
+
A list of CircuitToPauliStringsMeasurementResult objects, where each object contains:
|
|
309
|
+
- The circuit that was measured.
|
|
310
|
+
- A list of PauliStringMeasurementResult objects.
|
|
311
|
+
- The calibration result for single-qubit readout errors.
|
|
312
|
+
"""
|
|
313
|
+
|
|
314
|
+
_validate_input(
|
|
315
|
+
circuits_to_pauli,
|
|
316
|
+
pauli_repetitions,
|
|
317
|
+
readout_repetitions,
|
|
318
|
+
num_random_bitstrings,
|
|
319
|
+
rng_or_seed,
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
# Extract unique qubit tuples from input pauli strings
|
|
323
|
+
unique_qubit_tuples = set()
|
|
324
|
+
for pauli_strings in circuits_to_pauli.values():
|
|
325
|
+
for pauli_string in pauli_strings:
|
|
326
|
+
unique_qubit_tuples.add(tuple(sorted(pauli_string.qubits)))
|
|
327
|
+
# qubits_list is a list of qubit tuples
|
|
328
|
+
qubits_list = sorted(unique_qubit_tuples)
|
|
329
|
+
|
|
330
|
+
# Build the basis-change circuits for each Pauli string
|
|
331
|
+
pauli_measurement_circuits = list[circuits.Circuit]()
|
|
332
|
+
for input_circuit, pauli_strings in circuits_to_pauli.items():
|
|
333
|
+
qid_list = list(sorted(input_circuit.all_qubits()))
|
|
334
|
+
basis_change_circuits = []
|
|
335
|
+
input_circuit_unfrozen = input_circuit.unfreeze()
|
|
336
|
+
for pauli_string in pauli_strings:
|
|
337
|
+
basis_change_circuit = (
|
|
338
|
+
input_circuit_unfrozen
|
|
339
|
+
+ _pauli_string_to_basis_change_ops(pauli_string, qid_list)
|
|
340
|
+
+ ops.measure(*qid_list, key="m")
|
|
341
|
+
)
|
|
342
|
+
basis_change_circuits.append(basis_change_circuit)
|
|
343
|
+
pauli_measurement_circuits.extend(basis_change_circuits)
|
|
344
|
+
|
|
345
|
+
# Run shuffled benchmarking for readout calibration
|
|
346
|
+
circuits_results, calibration_results = run_shuffled_with_readout_benchmarking(
|
|
347
|
+
input_circuits=pauli_measurement_circuits,
|
|
348
|
+
sampler=sampler,
|
|
349
|
+
circuit_repetitions=pauli_repetitions,
|
|
350
|
+
rng_or_seed=rng_or_seed,
|
|
351
|
+
qubits=[list(qubits) for qubits in qubits_list],
|
|
352
|
+
num_random_bitstrings=num_random_bitstrings,
|
|
353
|
+
readout_repetitions=readout_repetitions,
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
# Process the results to calculate expectation values
|
|
357
|
+
results: List[CircuitToPauliStringsMeasurementResult] = []
|
|
358
|
+
circuit_result_index = 0
|
|
359
|
+
for input_circuit, pauli_strings in circuits_to_pauli.items():
|
|
360
|
+
qubits_in_circuit = tuple(sorted(input_circuit.all_qubits()))
|
|
361
|
+
|
|
362
|
+
disable_readout_mitigation = False if num_random_bitstrings != 0 else True
|
|
363
|
+
pauli_measurement_results = _process_pauli_measurement_results(
|
|
364
|
+
list(qubits_in_circuit),
|
|
365
|
+
pauli_strings,
|
|
366
|
+
circuits_results[circuit_result_index : circuit_result_index + len(pauli_strings)],
|
|
367
|
+
calibration_results,
|
|
368
|
+
pauli_repetitions,
|
|
369
|
+
time.time(),
|
|
370
|
+
disable_readout_mitigation,
|
|
371
|
+
)
|
|
372
|
+
results.append(
|
|
373
|
+
CircuitToPauliStringsMeasurementResult(
|
|
374
|
+
circuit=input_circuit, results=pauli_measurement_results
|
|
375
|
+
)
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
circuit_result_index += len(pauli_strings)
|
|
379
|
+
return results
|