cirq-core 1.5.0.dev20240628073059__py3-none-any.whl → 1.5.0.dev20240701205135__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/experiments/readout_confusion_matrix.py +82 -2
- cirq/experiments/readout_confusion_matrix_test.py +103 -0
- {cirq_core-1.5.0.dev20240628073059.dist-info → cirq_core-1.5.0.dev20240701205135.dist-info}/METADATA +1 -1
- {cirq_core-1.5.0.dev20240628073059.dist-info → cirq_core-1.5.0.dev20240701205135.dist-info}/RECORD +9 -9
- {cirq_core-1.5.0.dev20240628073059.dist-info → cirq_core-1.5.0.dev20240701205135.dist-info}/LICENSE +0 -0
- {cirq_core-1.5.0.dev20240628073059.dist-info → cirq_core-1.5.0.dev20240701205135.dist-info}/WHEEL +0 -0
- {cirq_core-1.5.0.dev20240628073059.dist-info → cirq_core-1.5.0.dev20240701205135.dist-info}/top_level.txt +0 -0
cirq/_version.py
CHANGED
cirq/_version_test.py
CHANGED
|
@@ -26,12 +26,26 @@ if TYPE_CHECKING:
|
|
|
26
26
|
import cirq
|
|
27
27
|
|
|
28
28
|
|
|
29
|
+
def _mitigate_single_bitstring(z_rinv_all: list[np.ndarray], bitstring: np.ndarray) -> float:
|
|
30
|
+
"""Return the mitigated Pauli expectation value for a single observed bitstring.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
z_rinv_all: A list of single-qubit pauli-Z (as a vector) contracted with the single-qubit
|
|
34
|
+
inverse response matrix, for each qubit.
|
|
35
|
+
bitstring: The measured bitstring.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
The corrected expectation value of ZZZZZ... given the single measured bitstring.
|
|
39
|
+
"""
|
|
40
|
+
return np.prod([z_rinv[bit] for z_rinv, bit in zip(z_rinv_all, bitstring)])
|
|
41
|
+
|
|
42
|
+
|
|
29
43
|
class TensoredConfusionMatrices:
|
|
30
44
|
"""Store and use confusion matrices for readout error mitigation on sets of qubits.
|
|
31
45
|
|
|
32
46
|
The confusion matrix (CM) for one qubit is:
|
|
33
47
|
|
|
34
|
-
[ Pr(0|0) Pr(1
|
|
48
|
+
[ Pr(0|0) Pr(0|1) ]
|
|
35
49
|
[ Pr(1|0) Pr(1|1) ]
|
|
36
50
|
|
|
37
51
|
where Pr(i | j) = Probability of observing state "i" given state "j" was prepared.
|
|
@@ -71,7 +85,7 @@ class TensoredConfusionMatrices:
|
|
|
71
85
|
confusion_matrices: Sequence of confusion matrices, computed for qubit patterns present
|
|
72
86
|
in `measure_qubits`. A single confusion matrix is also accepted.
|
|
73
87
|
measure_qubits: Sequence of smaller qubit patterns, for which the confusion matrices
|
|
74
|
-
were computed. A single qubit pattern is also accepted. Note that
|
|
88
|
+
were computed. A single qubit pattern is also accepted. Note that
|
|
75
89
|
each qubit pattern is a sequence of qubits used to label the axes of
|
|
76
90
|
the corresponding confusion matrix.
|
|
77
91
|
repetitions: The number of repetitions that were used to estimate the confusion
|
|
@@ -298,6 +312,72 @@ class TensoredConfusionMatrices:
|
|
|
298
312
|
) # pragma: no cover
|
|
299
313
|
return res.x
|
|
300
314
|
|
|
315
|
+
def readout_mitigation_pauli_uncorrelated(
|
|
316
|
+
self, qubits: Sequence['cirq.Qid'], measured_bitstrings: np.ndarray
|
|
317
|
+
) -> tuple[float, float]:
|
|
318
|
+
r"""Uncorrelated readout error mitigation for a multi-qubit Pauli operator.
|
|
319
|
+
|
|
320
|
+
This function scalably performs readout error mitigation on an arbitrary-length Pauli
|
|
321
|
+
operator. It is a reimplementation of https://github.com/eliottrosenberg/correlated_SPAM
|
|
322
|
+
but specialized to the case in which readout is uncorrelated. We require that the confusion
|
|
323
|
+
matrix is a tensor product of single-qubit confusion matrices. We then invert the confusion
|
|
324
|
+
matrix by inverting each of the $C^{(q)}$ Then, in a bit-by-bit fashion, we apply the
|
|
325
|
+
inverses of the single-site confusion matrices to the bits of the measured bitstring,
|
|
326
|
+
contract them with the single-site Pauli operator, and take the product over all of the
|
|
327
|
+
bits. This could be generalized to tensor product spaces that are larger than single qubits,
|
|
328
|
+
but the essential simplification is that each tensor product space is small, so that none of
|
|
329
|
+
the response matrices is exponentially large.
|
|
330
|
+
|
|
331
|
+
This can result in mitigated Pauli operators that are not in the range [-1, 1], but if
|
|
332
|
+
the readout error is indeed uncorrelated and well-characterized, then it should converge
|
|
333
|
+
to being within this range. Results are improved both by a more precise characterization
|
|
334
|
+
of the response matrices (whose statistical uncertainty is not accounted for in the error
|
|
335
|
+
propagation here) and by increasing the number of measured bitstrings.
|
|
336
|
+
|
|
337
|
+
Args:
|
|
338
|
+
qubits: The qubits on which the Pauli operator acts.
|
|
339
|
+
measured_bitstrings: The experimentally measured bitstrings in the eigenbasis of the
|
|
340
|
+
Pauli operator. measured_bitstrings[i,j] is the ith bitstring, qubit j.
|
|
341
|
+
|
|
342
|
+
Returns:
|
|
343
|
+
The error-mitigated expectation value of the Pauli operator and its statistical
|
|
344
|
+
uncertainty (not including the uncertainty in the confusion matrices for now).
|
|
345
|
+
|
|
346
|
+
Raises:
|
|
347
|
+
NotImplementedError: If the confusion matrix is not a tensor product of single-qubit
|
|
348
|
+
confusion matrices for all of `qubits`.
|
|
349
|
+
"""
|
|
350
|
+
|
|
351
|
+
# first, get all of the confusion matrices
|
|
352
|
+
cm_all = []
|
|
353
|
+
for qubit in qubits:
|
|
354
|
+
try:
|
|
355
|
+
idx = self.measure_qubits.index((qubit,))
|
|
356
|
+
except: # pragma: no cover
|
|
357
|
+
raise NotImplementedError( # pragma: no cover
|
|
358
|
+
"The response matrix must be a tensor product of single-qu" # pragma: no cover
|
|
359
|
+
+ f"bit response matrices, including that of qubit {qubit}." # pragma: no cover
|
|
360
|
+
) # pragma: no cover
|
|
361
|
+
cm_all.append(self.confusion_matrices[idx])
|
|
362
|
+
|
|
363
|
+
# get the correction matrices, assuming uncorrelated readout:
|
|
364
|
+
cminv_all = [np.linalg.inv(cm) for cm in cm_all]
|
|
365
|
+
|
|
366
|
+
# next, contract them with the single-qubit Pauli operators:
|
|
367
|
+
z = np.array([1, -1])
|
|
368
|
+
z_cminv_all = [z @ cminv for cminv in cminv_all]
|
|
369
|
+
|
|
370
|
+
# finally, mitigate each bitstring:
|
|
371
|
+
z_mit_all_shots = np.array(
|
|
372
|
+
[
|
|
373
|
+
_mitigate_single_bitstring(z_cminv_all, bitstring)
|
|
374
|
+
for bitstring in measured_bitstrings
|
|
375
|
+
]
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
# return mean and statistical uncertainty:
|
|
379
|
+
return np.mean(z_mit_all_shots), np.std(z_mit_all_shots) / np.sqrt(len(measured_bitstrings))
|
|
380
|
+
|
|
301
381
|
def __repr__(self) -> str:
|
|
302
382
|
return (
|
|
303
383
|
f"cirq.TensoredConfusionMatrices("
|
|
@@ -17,6 +17,34 @@ import cirq
|
|
|
17
17
|
import pytest
|
|
18
18
|
|
|
19
19
|
from cirq.experiments.single_qubit_readout_calibration_test import NoisySingleQubitReadoutSampler
|
|
20
|
+
from cirq.experiments.readout_confusion_matrix import TensoredConfusionMatrices
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def add_readout_error(
|
|
24
|
+
measurements: np.ndarray,
|
|
25
|
+
zero_errors: np.ndarray,
|
|
26
|
+
one_errors: np.ndarray,
|
|
27
|
+
rng: np.random.Generator,
|
|
28
|
+
) -> np.ndarray:
|
|
29
|
+
"""Add readout errors to measured (or simulated) bitstrings.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
measurements: The bitstrings to which we will add readout errors. measurements[i,j] is the
|
|
33
|
+
ith bitstring, qubit j.
|
|
34
|
+
zero_errors: zero_errors[i] is the probability of a 0->1 readout error on qubit i.
|
|
35
|
+
one_errors: one_errors[i] is the probability of a 1->0 readout error on qubit i.
|
|
36
|
+
rng: The pseudorandom number generator to use.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
New measurements but with readout errors added.
|
|
40
|
+
"""
|
|
41
|
+
num_bitstrs, n = measurements.shape
|
|
42
|
+
assert len(zero_errors) == len(one_errors) == n
|
|
43
|
+
# compute the probability that each bit is 1 after adding readout errors:
|
|
44
|
+
p1 = measurements * (1 - one_errors) + (1 - measurements) * zero_errors
|
|
45
|
+
r = rng.random((num_bitstrs, n))
|
|
46
|
+
noisy_measurements = r < p1
|
|
47
|
+
return noisy_measurements.astype(int)
|
|
20
48
|
|
|
21
49
|
|
|
22
50
|
def get_expected_cm(num_qubits: int, p0: float, p1: float):
|
|
@@ -159,3 +187,78 @@ def test_readout_confusion_matrix_repr_and_equality():
|
|
|
159
187
|
eq.add_equality_group(a, a)
|
|
160
188
|
eq.add_equality_group(b, b)
|
|
161
189
|
eq.add_equality_group(c, c)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def _sample_ghz(n: int, repetitions: int, rng: np.random.Generator) -> np.ndarray:
|
|
193
|
+
"""Sample a GHZ state in the z basis.
|
|
194
|
+
Args:
|
|
195
|
+
n: The number of qubits.
|
|
196
|
+
repetitions: The number of repetitions.
|
|
197
|
+
rng: The pseudorandom number generator to use.
|
|
198
|
+
Returns:
|
|
199
|
+
An array of the measurement outcomes.
|
|
200
|
+
"""
|
|
201
|
+
return np.tile(rng.integers(0, 2, size=repetitions), (n, 1)).T
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def _add_noise_and_mitigate_ghz(
|
|
205
|
+
n: int,
|
|
206
|
+
repetitions: int,
|
|
207
|
+
zero_errors: np.ndarray,
|
|
208
|
+
one_errors: np.ndarray,
|
|
209
|
+
rng: np.random.Generator | None = None,
|
|
210
|
+
) -> tuple[float, float, float, float]:
|
|
211
|
+
"""Add readout error to GHZ-like bitstrings and measure <ZZZ...> with and
|
|
212
|
+
without readout error mitigation.
|
|
213
|
+
Args:
|
|
214
|
+
n: The number of qubits.
|
|
215
|
+
repetitions: The number of repetitions.
|
|
216
|
+
zero_errors: zero_errors[i] is the probability of a 0->1 readout error on qubit i.
|
|
217
|
+
one_errors: one_errors[i] is the probability of a 1->0 readout error on qubit i.
|
|
218
|
+
rng: The pseudorandom number generator to use.
|
|
219
|
+
Returns:
|
|
220
|
+
A tuple of:
|
|
221
|
+
- The mitigated expectation value of <ZZZ...>
|
|
222
|
+
- The statistical uncertainty of the previous output
|
|
223
|
+
- The unmitigated expectation value of <ZZZ...>
|
|
224
|
+
- The statstical uncertainty of the previous output
|
|
225
|
+
"""
|
|
226
|
+
if rng is None:
|
|
227
|
+
rng = np.random.default_rng(0)
|
|
228
|
+
confusion_matrices = [
|
|
229
|
+
np.array([[1 - e0, e1], [e0, 1 - e1]]) for e0, e1 in zip(zero_errors, one_errors)
|
|
230
|
+
]
|
|
231
|
+
qubits = cirq.LineQubit.range(n)
|
|
232
|
+
tcm = TensoredConfusionMatrices(
|
|
233
|
+
confusion_matrices, [[q] for q in qubits], repetitions=0, timestamp=0.0
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
measurements = _sample_ghz(n, repetitions, rng)
|
|
237
|
+
noisy_measurements = add_readout_error(measurements, zero_errors, one_errors, rng)
|
|
238
|
+
# unmitigated:
|
|
239
|
+
p1 = np.mean(np.sum(noisy_measurements, axis=1) % 2)
|
|
240
|
+
z = 1 - 2 * np.mean(p1)
|
|
241
|
+
dz = 2 * np.sqrt(p1 * (1 - p1) / repetitions)
|
|
242
|
+
# return mitigated and unmitigated:
|
|
243
|
+
return (*tcm.readout_mitigation_pauli_uncorrelated(qubits, noisy_measurements), z, dz)
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def test_uncorrelated_readout_mitigation_pauli():
|
|
247
|
+
n_all = np.arange(2, 35)
|
|
248
|
+
z_all_mit = []
|
|
249
|
+
dz_all_mit = []
|
|
250
|
+
z_all_raw = []
|
|
251
|
+
dz_all_raw = []
|
|
252
|
+
repetitions = 10_000
|
|
253
|
+
for n in n_all:
|
|
254
|
+
e0 = np.ones(n) * 0.005
|
|
255
|
+
e1 = np.ones(n) * 0.03
|
|
256
|
+
z_mit, dz_mit, z_raw, dz_raw = _add_noise_and_mitigate_ghz(n, repetitions, e0, e1)
|
|
257
|
+
z_all_mit.append(z_mit)
|
|
258
|
+
dz_all_mit.append(dz_mit)
|
|
259
|
+
z_all_raw.append(z_raw)
|
|
260
|
+
dz_all_raw.append(dz_raw)
|
|
261
|
+
|
|
262
|
+
for n, z, dz in zip(n_all, z_all_mit, dz_all_mit):
|
|
263
|
+
ideal = 1.0 if n % 2 == 0 else 0.0
|
|
264
|
+
assert np.isclose(z, ideal, atol=4 * dz)
|
{cirq_core-1.5.0.dev20240628073059.dist-info → cirq_core-1.5.0.dev20240701205135.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: cirq-core
|
|
3
|
-
Version: 1.5.0.
|
|
3
|
+
Version: 1.5.0.dev20240701205135
|
|
4
4
|
Summary: A framework for creating, editing, and invoking Noisy Intermediate Scale Quantum (NISQ) circuits.
|
|
5
5
|
Home-page: http://github.com/quantumlib/cirq
|
|
6
6
|
Author: The Cirq Developers
|
{cirq_core-1.5.0.dev20240628073059.dist-info → cirq_core-1.5.0.dev20240701205135.dist-info}/RECORD
RENAMED
|
@@ -4,8 +4,8 @@ cirq/_compat_test.py,sha256=Qq3ZcfgD-Nb81cEppQdJqhAyrVqXKtfXZYGXT0p-Wh0,34718
|
|
|
4
4
|
cirq/_doc.py,sha256=yDyWUD_2JDS0gShfGRb-rdqRt9-WeL7DhkqX7np0Nko,2879
|
|
5
5
|
cirq/_import.py,sha256=p9gMHJscbtDDkfHOaulvd3Aer0pwUF5AXpL89XR8dNw,8402
|
|
6
6
|
cirq/_import_test.py,sha256=6K_v0riZJXOXUphHNkGA8MY-JcmGlezFaGmvrNhm3OQ,1015
|
|
7
|
-
cirq/_version.py,sha256=
|
|
8
|
-
cirq/_version_test.py,sha256=
|
|
7
|
+
cirq/_version.py,sha256=_zIQ22vgNE_xJZVUgRlo8jd8osD7HKMrfP55L4B-u4g,1206
|
|
8
|
+
cirq/_version_test.py,sha256=blpx6eLf7bpTF3Ruw7yOsVnbfplWIhhwfMZXaTfvdcA,147
|
|
9
9
|
cirq/conftest.py,sha256=X7yLFL8GLhg2CjPw0hp5e_dGASfvHx1-QT03aUbhKJw,1168
|
|
10
10
|
cirq/json_resolver_cache.py,sha256=ytePZtNZgKjOF2NiVpUTuotB-JKZmQNOFIFdvXqsxHw,13271
|
|
11
11
|
cirq/py.typed,sha256=VFSlmh_lNwnaXzwY-ZuW-C2Ws5PkuDoVgBdNCs0jXJE,63
|
|
@@ -188,8 +188,8 @@ cirq/experiments/qubit_characterizations.py,sha256=Zi925SVZ0U5HbkyuGD6BspjzPAhsq
|
|
|
188
188
|
cirq/experiments/qubit_characterizations_test.py,sha256=b_ONqxyW6s01Ts8T65BEdb4e8Xy24Qp4zTGXWesL0ic,9733
|
|
189
189
|
cirq/experiments/random_quantum_circuit_generation.py,sha256=R_w7z35plUHEYBY0-F80bPcWJSSSjNQDaP2GbxVBEZg,28143
|
|
190
190
|
cirq/experiments/random_quantum_circuit_generation_test.py,sha256=1rvgN8-Ajedn_70FyYKVzjvzR6NVpHj6KQgo6tra-Jc,15995
|
|
191
|
-
cirq/experiments/readout_confusion_matrix.py,sha256=
|
|
192
|
-
cirq/experiments/readout_confusion_matrix_test.py,sha256=
|
|
191
|
+
cirq/experiments/readout_confusion_matrix.py,sha256=jIyikXfYWGbVrOjU1pbV2VZL-m03VTFYS18KT1Cf2qk,21404
|
|
192
|
+
cirq/experiments/readout_confusion_matrix_test.py,sha256=ETvKHVuJWvq8KuL0l6w22UOfZHhBNH-TVeWAKqjSQEc,10632
|
|
193
193
|
cirq/experiments/single_qubit_readout_calibration.py,sha256=ZmfsARZ_33Pg4lh5toJaoi4h4RGKHISAfWBODq1Icsg,14599
|
|
194
194
|
cirq/experiments/single_qubit_readout_calibration_test.py,sha256=_002QXj2rIFHkH3vw9iTVMh45vCPuCI_fTqOUK8uMe4,7718
|
|
195
195
|
cirq/experiments/t1_decay_experiment.py,sha256=ealdmc_RTE__z1YUcaDEncDzQOaiT0K6IRWB7lNtPfs,7087
|
|
@@ -1175,8 +1175,8 @@ cirq/work/sampler.py,sha256=JEAeQQRF3bqlO9AkOf4XbrTATDI5f5JgyM_FAUCNxao,19751
|
|
|
1175
1175
|
cirq/work/sampler_test.py,sha256=B2ZsuqGT854gQtBIAh8k0LiG9Vj5wSzcGvkxOUoTcW4,13217
|
|
1176
1176
|
cirq/work/zeros_sampler.py,sha256=x1C7cup66a43n-3tm8QjhiqJa07qcJW10FxNp9jJ59Q,2356
|
|
1177
1177
|
cirq/work/zeros_sampler_test.py,sha256=JIkpBBFPJe5Ba4142vzogyWyboG1Q1ZAm0UVGgOoZn8,3279
|
|
1178
|
-
cirq_core-1.5.0.
|
|
1179
|
-
cirq_core-1.5.0.
|
|
1180
|
-
cirq_core-1.5.0.
|
|
1181
|
-
cirq_core-1.5.0.
|
|
1182
|
-
cirq_core-1.5.0.
|
|
1178
|
+
cirq_core-1.5.0.dev20240701205135.dist-info/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
|
|
1179
|
+
cirq_core-1.5.0.dev20240701205135.dist-info/METADATA,sha256=TCy1YUSNHCmnT1kjhFUi8jyxdSWvfLdlT9634-F2HK8,2007
|
|
1180
|
+
cirq_core-1.5.0.dev20240701205135.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
1181
|
+
cirq_core-1.5.0.dev20240701205135.dist-info/top_level.txt,sha256=Sz9iOxHU0IEMLSFGwiwOCaN2e9K-jFbBbtpPN1hB73g,5
|
|
1182
|
+
cirq_core-1.5.0.dev20240701205135.dist-info/RECORD,,
|
{cirq_core-1.5.0.dev20240628073059.dist-info → cirq_core-1.5.0.dev20240701205135.dist-info}/LICENSE
RENAMED
|
File without changes
|
{cirq_core-1.5.0.dev20240628073059.dist-info → cirq_core-1.5.0.dev20240701205135.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|