cirq-core 1.7.0.dev20250801234137__py3-none-any.whl → 1.7.0.dev20250802153528__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 CHANGED
@@ -28,4 +28,4 @@ if sys.version_info < (3, 11, 0): # pragma: no cover
28
28
  'of Cirq (e.g. "python -m pip install cirq==1.5.0")'
29
29
  )
30
30
 
31
- __version__ = "1.7.0.dev20250801234137"
31
+ __version__ = "1.7.0.dev20250802153528"
cirq/_version_test.py CHANGED
@@ -3,4 +3,4 @@ import cirq
3
3
 
4
4
 
5
5
  def test_version() -> None:
6
- assert cirq.__version__ == "1.7.0.dev20250801234137"
6
+ assert cirq.__version__ == "1.7.0.dev20250802153528"
@@ -23,8 +23,8 @@ from typing import cast, Sequence, TYPE_CHECKING
23
23
  import attrs
24
24
  import numpy as np
25
25
 
26
- from cirq import circuits, ops, work
27
- from cirq.contrib.shuffle_circuits import run_shuffled_with_readout_benchmarking
26
+ import cirq.contrib.shuffle_circuits.shuffle_circuits_with_readout_benchmarking as sc_readout
27
+ from cirq import circuits, ops, study, work
28
28
  from cirq.experiments.readout_confusion_matrix import TensoredConfusionMatrices
29
29
 
30
30
  if TYPE_CHECKING:
@@ -288,7 +288,7 @@ def _build_many_one_qubits_empty_confusion_matrix(qubits_length: int) -> list[np
288
288
  def _process_pauli_measurement_results(
289
289
  qubits: Sequence[ops.Qid],
290
290
  pauli_string_groups: list[list[ops.PauliString]],
291
- circuit_results: list[ResultDict],
291
+ circuit_results: list[ResultDict] | Sequence[study.Result],
292
292
  calibration_results: dict[tuple[ops.Qid, ...], SingleQubitReadoutCalibrationResult],
293
293
  pauli_repetitions: int,
294
294
  timestamp: float,
@@ -474,14 +474,18 @@ def measure_pauli_strings(
474
474
  pauli_measurement_circuits.extend(basis_change_circuits)
475
475
 
476
476
  # Run shuffled benchmarking for readout calibration
477
- circuits_results, calibration_results = run_shuffled_with_readout_benchmarking(
478
- input_circuits=pauli_measurement_circuits,
479
- sampler=sampler,
480
- circuit_repetitions=pauli_repetitions,
481
- rng_or_seed=rng_or_seed,
482
- qubits=[list(qubits) for qubits in qubits_list],
483
- num_random_bitstrings=num_random_bitstrings,
484
- readout_repetitions=readout_repetitions,
477
+ circuits_results, calibration_results = (
478
+ sc_readout.run_shuffled_circuits_with_readout_benchmarking(
479
+ sampler=sampler,
480
+ input_circuits=pauli_measurement_circuits,
481
+ parameters=sc_readout.ReadoutBenchmarkingParams(
482
+ circuit_repetitions=pauli_repetitions,
483
+ num_random_bitstrings=num_random_bitstrings,
484
+ readout_repetitions=readout_repetitions,
485
+ ),
486
+ rng_or_seed=rng_or_seed,
487
+ qubits=[list(qubits) for qubits in qubits_list],
488
+ )
485
489
  )
486
490
 
487
491
  # Process the results to calculate expectation values
@@ -895,7 +895,7 @@ def test_process_pauli_measurement_results_raises_error_on_missing_calibration()
895
895
  _process_pauli_measurement_results(
896
896
  qubits,
897
897
  [pauli_strings],
898
- circuit_results[0], # type: ignore[arg-type]
898
+ circuit_results[0],
899
899
  empty_calibration_result_dict, # type: ignore[arg-type]
900
900
  1000,
901
901
  1.0,
@@ -15,4 +15,6 @@
15
15
 
16
16
  from cirq.contrib.shuffle_circuits.shuffle_circuits_with_readout_benchmarking import (
17
17
  run_shuffled_with_readout_benchmarking as run_shuffled_with_readout_benchmarking,
18
+ run_shuffled_circuits_with_readout_benchmarking as run_shuffled_circuits_with_readout_benchmarking, # noqa: E501
19
+ run_sweep_with_readout_benchmarking as run_sweep_with_readout_benchmarking,
18
20
  )
@@ -17,23 +17,54 @@
17
17
  from __future__ import annotations
18
18
 
19
19
  import time
20
- from typing import TYPE_CHECKING
20
+ from typing import Optional, Sequence, TYPE_CHECKING
21
21
 
22
+ import attrs
22
23
  import numpy as np
24
+ import sympy
23
25
 
24
- from cirq import circuits, ops, protocols, work
26
+ from cirq import circuits, ops, protocols, study, work
27
+ from cirq._compat import deprecated
25
28
  from cirq.experiments import SingleQubitReadoutCalibrationResult
26
29
 
27
30
  if TYPE_CHECKING:
28
31
  from cirq.study import ResultDict
29
32
 
30
33
 
31
- def _validate_input(
32
- input_circuits: list[circuits.Circuit],
34
+ @attrs.frozen
35
+ class ReadoutBenchmarkingParams:
36
+ """Parameters for configuring readout benchmarking.
37
+
38
+ Attributes:
39
+ circuit_repetitions: The repetitions for `circuits`.
40
+ num_random_bitstrings: The number of random bitstrings for measuring readout.
41
+ If set to 0, no readout calibration circuits are generated.
42
+ readout_repetitions: The number of repetitions for each readout bitstring.
43
+ """
44
+
45
+ circuit_repetitions: int | list[int]
46
+ num_random_bitstrings: int = 100
47
+ readout_repetitions: int = 1000
48
+
49
+ def __attrs_post_init__(self):
50
+ # Check circuit_repetitions
51
+ if isinstance(self.circuit_repetitions, int):
52
+ if self.circuit_repetitions <= 0:
53
+ raise ValueError("Must provide non-zero circuit_repetitions.")
54
+
55
+ # Check num_random_bitstrings is bigger than or equal to 0
56
+ if self.num_random_bitstrings < 0:
57
+ raise ValueError("Must provide zero or more num_random_bitstrings.")
58
+
59
+ # Check readout_repetitions is bigger than 0
60
+ if self.readout_repetitions <= 0:
61
+ raise ValueError("Must provide non-zero readout_repetitions for readout calibration.")
62
+
63
+
64
+ def _validate_experiment_input(
65
+ input_circuits: Sequence[circuits.Circuit],
33
66
  circuit_repetitions: int | list[int],
34
- rng_or_seed: np.random.Generator | int,
35
- num_random_bitstrings: int,
36
- readout_repetitions: int,
67
+ rng_or_seed: Optional[np.random.Generator | int] = None,
37
68
  ):
38
69
  if not input_circuits:
39
70
  raise ValueError("Input circuits must not be empty.")
@@ -45,28 +76,24 @@ def _validate_input(
45
76
  if not any(protocols.is_measurement(circuit) for op in circuit.all_operations()):
46
77
  raise ValueError("Input circuits must have measurements.")
47
78
 
48
- # Check circuit_repetitions
49
- if isinstance(circuit_repetitions, int):
50
- if circuit_repetitions <= 0:
51
- raise ValueError("Must provide non-zero circuit_repetitions.")
52
79
  if isinstance(circuit_repetitions, list) and len(circuit_repetitions) != len(input_circuits):
53
80
  raise ValueError("Number of circuit_repetitions must match the number of input circuits.")
54
81
 
55
- # Check rng is a numpy random generator
56
- if not isinstance(rng_or_seed, np.random.Generator) and not isinstance(rng_or_seed, int):
57
- raise ValueError("Must provide a numpy random generator or a seed")
58
82
 
59
- # Check num_random_bitstrings is bigger than or equal to 0
60
- if num_random_bitstrings < 0:
61
- raise ValueError("Must provide zero or more num_random_bitstrings.")
62
-
63
- # Check readout_repetitions is bigger than 0
64
- if readout_repetitions <= 0:
65
- raise ValueError("Must provide non-zero readout_repetitions for readout calibration.")
83
+ def _validate_experiment_input_with_sweep(
84
+ input_circuits: Sequence[circuits.Circuit],
85
+ sweep_params: Sequence[study.Sweepable],
86
+ circuit_repetitions: int | list[int],
87
+ rng_or_seed: Optional[np.random.Generator | int] = None,
88
+ ):
89
+ """Validates the input for the run_sweep_with_readout_benchmarking function."""
90
+ if not sweep_params:
91
+ raise ValueError("Sweep parameters must not be empty.")
92
+ return _validate_experiment_input(input_circuits, circuit_repetitions, rng_or_seed)
66
93
 
67
94
 
68
95
  def _generate_readout_calibration_circuits(
69
- qubits: list[ops.Qid], rng: np.random.Generator, num_random_bitstrings: int
96
+ qubits: list[ops.Qid], num_random_bitstrings: int, rng: np.random.Generator
70
97
  ) -> tuple[list[circuits.Circuit], np.ndarray]:
71
98
  """Generates the readout calibration circuits with random bitstrings."""
72
99
  bit_to_gate = (ops.I, ops.X)
@@ -84,6 +111,95 @@ def _generate_readout_calibration_circuits(
84
111
  return readout_calibration_circuits, random_bitstrings
85
112
 
86
113
 
114
+ def _generate_parameterized_readout_calibration_circuit_with_sweep(
115
+ qubits: list[ops.Qid], num_random_bitstrings: int, rng: np.random.Generator
116
+ ) -> tuple[circuits.Circuit, study.Sweepable, np.ndarray]:
117
+ """Generates a parameterized readout calibration circuit, sweep parameters,
118
+ and the random bitstrings.
119
+
120
+ The function generates a single cirq.Circuit with parameterized X gates.
121
+ The function also generates a set of random bitstrings and creates a list
122
+ of sweep parameters to map the parameters in the circuit to the values in
123
+ each bitstring, allowing efficient calibration of readout errors of input qubits.
124
+
125
+ Args:
126
+ qubits: The list of qubits to include in the calibration circuit.
127
+ num_random_bitstrings: The number of random bitstrings to generate for calibration.
128
+ rng: A numpy random number generator used to generate the random bitstrings.
129
+
130
+ Returns:
131
+ A tuple containing:
132
+ - The parameterized readout calibration circuit (cirq.Circuit).
133
+ - A list of parameter sweeps (one for each random bitstring).
134
+ - The numpy array of generated random bitstrings.
135
+ """
136
+ random_bitstrings = rng.integers(0, 2, size=(num_random_bitstrings, len(qubits)))
137
+
138
+ exp_symbols = [sympy.Symbol(f'exp_{qubit}') for qubit in qubits]
139
+ parameterized_readout_calibration_circuit = circuits.Circuit(
140
+ [ops.X(qubit) ** exp for exp, qubit in zip(exp_symbols, qubits)], ops.M(*qubits, key="m")
141
+ )
142
+ sweep_params = []
143
+ for bitstr in random_bitstrings:
144
+ sweep_params.append({exp: bit for exp, bit in zip(exp_symbols, bitstr)})
145
+
146
+ return parameterized_readout_calibration_circuit, sweep_params, random_bitstrings
147
+
148
+
149
+ def _generate_all_readout_calibration_circuits(
150
+ num_random_bitstrings: int,
151
+ qubits_to_measure: list[list[ops.Qid]],
152
+ is_sweep: bool,
153
+ rng: np.random.Generator,
154
+ ) -> tuple[list[circuits.Circuit], list[np.ndarray], list[study.Sweepable]]:
155
+ """Generates all readout calibration circuits and random bitstrings."""
156
+ all_readout_calibration_circuits: list[circuits.Circuit] = []
157
+ all_random_bitstrings: list[np.ndarray] = []
158
+ all_readout_sweep_params: list[study.Sweepable] = []
159
+
160
+ if num_random_bitstrings <= 0:
161
+ return all_readout_calibration_circuits, all_random_bitstrings, all_readout_sweep_params
162
+
163
+ if not is_sweep:
164
+ for qubit_group in qubits_to_measure:
165
+ readout_calibration_circuits, random_bitstrings = (
166
+ _generate_readout_calibration_circuits(qubit_group, num_random_bitstrings, rng)
167
+ )
168
+ all_readout_calibration_circuits.extend(readout_calibration_circuits)
169
+ all_random_bitstrings.append(random_bitstrings)
170
+ else:
171
+ for qubit_group in qubits_to_measure:
172
+ (parameterized_readout_calibration_circuit, readout_sweep_params, random_bitstrings) = (
173
+ _generate_parameterized_readout_calibration_circuit_with_sweep(
174
+ qubit_group, num_random_bitstrings, rng
175
+ )
176
+ )
177
+ all_readout_calibration_circuits.append(parameterized_readout_calibration_circuit)
178
+ all_readout_sweep_params.append([readout_sweep_params])
179
+ all_random_bitstrings.append(random_bitstrings)
180
+
181
+ return all_readout_calibration_circuits, all_random_bitstrings, all_readout_sweep_params
182
+
183
+
184
+ def _determine_qubits_to_measure(
185
+ input_circuits: Sequence[circuits.Circuit],
186
+ qubits: Optional[Sequence[ops.Qid] | Sequence[Sequence[ops.Qid]]],
187
+ ) -> list[list[ops.Qid]]:
188
+ """Determine the qubits to measure based on the input circuits and provided qubits."""
189
+ # If input qubits is None, extract qubits from input circuits
190
+ qubits_to_measure: list[list[ops.Qid]] = []
191
+ if qubits is None:
192
+ qubits_to_measure = [
193
+ sorted(set(q for circuit in input_circuits for q in circuit.all_qubits()))
194
+ ]
195
+
196
+ elif isinstance(qubits[0], ops.Qid):
197
+ qubits_to_measure = [qubits] # type: ignore
198
+ else:
199
+ qubits_to_measure = qubits # type: ignore
200
+ return qubits_to_measure
201
+
202
+
87
203
  def _shuffle_circuits(
88
204
  all_circuits: list[circuits.Circuit], all_repetitions: list[int], rng: np.random.Generator
89
205
  ) -> tuple[list[circuits.Circuit], list[int], np.ndarray]:
@@ -97,7 +213,7 @@ def _shuffle_circuits(
97
213
 
98
214
 
99
215
  def _analyze_readout_results(
100
- unshuffled_readout_measurements: list[ResultDict],
216
+ unshuffled_readout_measurements: Sequence[ResultDict] | Sequence[study.Result],
101
217
  random_bitstrings: np.ndarray,
102
218
  readout_repetitions: int,
103
219
  qubits: list[ops.Qid],
@@ -156,6 +272,7 @@ def _analyze_readout_results(
156
272
  )
157
273
 
158
274
 
275
+ @deprecated(deadline="v2.0", fix="Use run_shuffled_circuits_with_readout_benchmarking() instead.")
159
276
  def run_shuffled_with_readout_benchmarking(
160
277
  input_circuits: list[circuits.Circuit],
161
278
  sampler: work.Sampler,
@@ -163,8 +280,8 @@ def run_shuffled_with_readout_benchmarking(
163
280
  rng_or_seed: np.random.Generator | int,
164
281
  num_random_bitstrings: int = 100,
165
282
  readout_repetitions: int = 1000,
166
- qubits: list[ops.Qid] | list[list[ops.Qid]] | None = None,
167
- ) -> tuple[list[ResultDict], dict[tuple[ops.Qid, ...], SingleQubitReadoutCalibrationResult]]:
283
+ qubits: Optional[Sequence[ops.Qid] | Sequence[Sequence[ops.Qid]]] = None,
284
+ ) -> tuple[Sequence[ResultDict], dict[tuple[ops.Qid, ...], SingleQubitReadoutCalibrationResult]]:
168
285
  """Run the circuits in a shuffled order with readout error benchmarking.
169
286
 
170
287
  Args:
@@ -187,39 +304,35 @@ def run_shuffled_with_readout_benchmarking(
187
304
 
188
305
  """
189
306
 
190
- _validate_input(
191
- input_circuits, circuit_repetitions, rng_or_seed, num_random_bitstrings, readout_repetitions
192
- )
307
+ # Check circuit_repetitions
308
+ if isinstance(circuit_repetitions, int):
309
+ if circuit_repetitions <= 0:
310
+ raise ValueError("Must provide non-zero circuit_repetitions.")
193
311
 
194
- # If input qubits is None, extract qubits from input circuits
195
- qubits_to_measure: list[list[ops.Qid]] = []
196
- if qubits is None:
197
- qubits_set: set[ops.Qid] = set()
198
- for circuit in input_circuits:
199
- qubits_set.update(circuit.all_qubits())
200
- qubits_to_measure = [sorted(qubits_set)]
201
- elif isinstance(qubits[0], ops.Qid):
202
- qubits_to_measure = [qubits] # type: ignore
203
- else:
204
- qubits_to_measure = qubits # type: ignore
312
+ # Check num_random_bitstrings is bigger than or equal to 0
313
+ if num_random_bitstrings < 0:
314
+ raise ValueError("Must provide zero or more num_random_bitstrings.")
315
+
316
+ # Check readout_repetitions is bigger than 0
317
+ if readout_repetitions <= 0:
318
+ raise ValueError("Must provide non-zero readout_repetitions for readout calibration.")
319
+ _validate_experiment_input(input_circuits, circuit_repetitions, rng_or_seed)
320
+
321
+ qubits_to_measure = _determine_qubits_to_measure(input_circuits, qubits)
205
322
 
206
323
  # Generate the readout calibration circuits if num_random_bitstrings>0
207
324
  # Else all_readout_calibration_circuits and all_random_bitstrings are empty
208
- all_readout_calibration_circuits = []
209
- all_random_bitstrings = []
210
-
211
325
  rng = (
212
326
  rng_or_seed
213
327
  if isinstance(rng_or_seed, np.random.Generator)
214
328
  else np.random.default_rng(rng_or_seed)
215
329
  )
216
- if num_random_bitstrings > 0:
217
- for qubit_group in qubits_to_measure:
218
- readout_calibration_circuits, random_bitstrings = (
219
- _generate_readout_calibration_circuits(qubit_group, rng, num_random_bitstrings)
220
- )
221
- all_readout_calibration_circuits.extend(readout_calibration_circuits)
222
- all_random_bitstrings.append(random_bitstrings)
330
+
331
+ all_readout_calibration_circuits, all_random_bitstrings, _ = (
332
+ _generate_all_readout_calibration_circuits(
333
+ num_random_bitstrings, qubits_to_measure, False, rng
334
+ )
335
+ )
223
336
 
224
337
  # Shuffle the circuits
225
338
  if isinstance(circuit_repetitions, int):
@@ -254,3 +367,173 @@ def run_shuffled_with_readout_benchmarking(
254
367
  start_idx = end_idx
255
368
 
256
369
  return unshuffled_input_circuits_measiurements, readout_calibration_results
370
+
371
+
372
+ def run_shuffled_circuits_with_readout_benchmarking(
373
+ sampler: work.Sampler,
374
+ input_circuits: list[circuits.Circuit],
375
+ parameters: ReadoutBenchmarkingParams,
376
+ qubits: Optional[Sequence[ops.Qid] | Sequence[Sequence[ops.Qid]]] = None,
377
+ rng_or_seed: Optional[np.random.Generator | int] = None,
378
+ ) -> tuple[Sequence[ResultDict], dict[tuple[ops.Qid, ...], SingleQubitReadoutCalibrationResult]]:
379
+ """Run the circuits in a shuffled order with readout error benchmarking.
380
+
381
+ Args:
382
+ sampler: The sampler to use.
383
+ input_circuits: The circuits to run.
384
+ parameters: The readout benchmarking parameters.
385
+ qubits: The qubits to benchmark readout errors. If None, all qubits in the
386
+ input_circuits are used. Can be a list of qubits or a list of tuples
387
+ of qubits.
388
+ rng_or_seed: A random number generator used to generate readout circuits.
389
+ Or an integer seed.
390
+
391
+ Returns:
392
+ A tuple containing:
393
+ - A list of dictionaries with the unshuffled measurement results.
394
+ - A dictionary mapping each tuple of qubits to a SingleQubitReadoutCalibrationResult.
395
+
396
+ """
397
+
398
+ _validate_experiment_input(input_circuits, parameters.circuit_repetitions, rng_or_seed)
399
+
400
+ qubits_to_measure = _determine_qubits_to_measure(input_circuits, qubits)
401
+
402
+ # Generate the readout calibration circuits if num_random_bitstrings>0
403
+ # Else all_readout_calibration_circuits and all_random_bitstrings are empty
404
+ rng = (
405
+ rng_or_seed
406
+ if isinstance(rng_or_seed, np.random.Generator)
407
+ else np.random.default_rng(rng_or_seed)
408
+ )
409
+
410
+ all_readout_calibration_circuits, all_random_bitstrings, _ = (
411
+ _generate_all_readout_calibration_circuits(
412
+ parameters.num_random_bitstrings, qubits_to_measure, False, rng
413
+ )
414
+ )
415
+
416
+ # Shuffle the circuits
417
+ circuit_repetitions = parameters.circuit_repetitions
418
+ if isinstance(circuit_repetitions, int):
419
+ circuit_repetitions = [circuit_repetitions] * len(input_circuits)
420
+ all_repetitions = circuit_repetitions + [parameters.readout_repetitions] * len(
421
+ all_readout_calibration_circuits
422
+ )
423
+
424
+ shuffled_circuits, all_repetitions, unshuf_order = _shuffle_circuits(
425
+ input_circuits + all_readout_calibration_circuits, all_repetitions, rng
426
+ )
427
+
428
+ # Run the shuffled circuits and measure
429
+ results = sampler.run_batch(shuffled_circuits, repetitions=all_repetitions)
430
+ timestamp = time.time()
431
+ shuffled_measurements = [res[0] for res in results]
432
+ unshuffled_measurements = [shuffled_measurements[i] for i in unshuf_order]
433
+
434
+ unshuffled_input_circuits_measiurements = unshuffled_measurements[: len(input_circuits)]
435
+ unshuffled_readout_measurements = unshuffled_measurements[len(input_circuits) :]
436
+
437
+ # Analyze results
438
+ readout_calibration_results = {}
439
+ start_idx = 0
440
+ for qubit_group, random_bitstrings in zip(qubits_to_measure, all_random_bitstrings):
441
+ end_idx = start_idx + len(random_bitstrings)
442
+ group_measurements = unshuffled_readout_measurements[start_idx:end_idx]
443
+ calibration_result = _analyze_readout_results(
444
+ group_measurements,
445
+ random_bitstrings,
446
+ parameters.readout_repetitions,
447
+ qubit_group,
448
+ timestamp,
449
+ )
450
+ readout_calibration_results[tuple(qubit_group)] = calibration_result
451
+ start_idx = end_idx
452
+
453
+ return unshuffled_input_circuits_measiurements, readout_calibration_results
454
+
455
+
456
+ def run_sweep_with_readout_benchmarking(
457
+ sampler: work.Sampler,
458
+ input_circuits: list[circuits.Circuit],
459
+ sweep_params: Sequence[study.Sweepable],
460
+ parameters: ReadoutBenchmarkingParams,
461
+ qubits: Optional[Sequence[ops.Qid] | Sequence[Sequence[ops.Qid]]] = None,
462
+ rng_or_seed: Optional[np.random.Generator | int] = None,
463
+ ) -> tuple[
464
+ Sequence[Sequence[study.Result]], dict[tuple[ops.Qid, ...], SingleQubitReadoutCalibrationResult]
465
+ ]:
466
+ """Run the sweep circuits with readout error benchmarking (no shuffling).
467
+ Args:
468
+ sampler: The sampler to use.
469
+ input_circuits: The circuits to run.
470
+ sweep_params: The sweep parameters for the input circuits.
471
+ parameters: The readout benchmarking parameters.
472
+ qubits: The qubits to benchmark readout errors. If None, all qubits in the
473
+ input_circuits are used. Can be a list of qubits or a list of tuples
474
+ of qubits.
475
+ rng_or_seed: A random number generator used to generate readout circuits.
476
+ Or an integer seed.
477
+
478
+ Returns:
479
+ A tuple containing:
480
+ - A list of lists of dictionaries with the measurement results.
481
+ - A dictionary mapping each tuple of qubits to a SingleQubitReadoutCalibrationResult.
482
+ """
483
+
484
+ _validate_experiment_input_with_sweep(
485
+ input_circuits, sweep_params, parameters.circuit_repetitions, rng_or_seed
486
+ )
487
+
488
+ qubits_to_measure = _determine_qubits_to_measure(input_circuits, qubits)
489
+
490
+ # Generate the readout calibration circuits (parameterized circuits) and sweep params
491
+ # if num_random_bitstrings>0
492
+ # Else all_readout_calibration_circuits and all_random_bitstrings are empty
493
+ rng = (
494
+ rng_or_seed
495
+ if isinstance(rng_or_seed, np.random.Generator)
496
+ else np.random.default_rng(rng_or_seed)
497
+ )
498
+
499
+ all_readout_calibration_circuits, all_random_bitstrings, all_readout_sweep_params = (
500
+ _generate_all_readout_calibration_circuits(
501
+ parameters.num_random_bitstrings, qubits_to_measure, True, rng
502
+ )
503
+ )
504
+
505
+ circuit_repetitions = parameters.circuit_repetitions
506
+ if isinstance(circuit_repetitions, int):
507
+ circuit_repetitions = [circuit_repetitions] * len(input_circuits)
508
+ all_repetitions = circuit_repetitions + [parameters.readout_repetitions] * len(
509
+ all_readout_calibration_circuits
510
+ )
511
+
512
+ # Run the sweep circuits and measure
513
+ results = sampler.run_batch(
514
+ input_circuits + all_readout_calibration_circuits,
515
+ list(sweep_params) + all_readout_sweep_params,
516
+ repetitions=all_repetitions,
517
+ )
518
+
519
+ timestamp = time.time()
520
+
521
+ input_circuits_measurement = results[: len(input_circuits)]
522
+ readout_measurements = results[len(input_circuits) :]
523
+
524
+ # Analyze results
525
+ readout_calibration_results = {}
526
+ for qubit_group, random_bitstrings, group_measurements in zip(
527
+ qubits_to_measure, all_random_bitstrings, readout_measurements
528
+ ):
529
+
530
+ calibration_result = _analyze_readout_results(
531
+ group_measurements,
532
+ random_bitstrings,
533
+ parameters.readout_repetitions,
534
+ qubit_group,
535
+ timestamp,
536
+ )
537
+ readout_calibration_results[tuple(qubit_group)] = calibration_result
538
+
539
+ return input_circuits_measurement, readout_calibration_results
@@ -15,11 +15,14 @@
15
15
  from __future__ import annotations
16
16
 
17
17
  import itertools
18
+ from typing import Sequence
18
19
 
19
20
  import numpy as np
20
21
  import pytest
22
+ import sympy
21
23
 
22
24
  import cirq
25
+ import cirq.contrib.shuffle_circuits.shuffle_circuits_with_readout_benchmarking as sc_readout
23
26
  from cirq.experiments import (
24
27
  random_quantum_circuit_generation as rqcg,
25
28
  SingleQubitReadoutCalibrationResult,
@@ -28,7 +31,7 @@ from cirq.experiments.single_qubit_readout_calibration_test import NoisySingleQu
28
31
  from cirq.study import ResultDict
29
32
 
30
33
 
31
- def _create_test_circuits(qubits: list[cirq.Qid], n_circuits: int) -> list[cirq.Circuit]:
34
+ def _create_test_circuits(qubits: Sequence[cirq.Qid], n_circuits: int) -> list[cirq.Circuit]:
32
35
  """Helper function to generate circuits for testing."""
33
36
  if len(qubits) < 2:
34
37
  raise ValueError(
@@ -50,32 +53,106 @@ def _create_test_circuits(qubits: list[cirq.Qid], n_circuits: int) -> list[cirq.
50
53
  return input_circuits
51
54
 
52
55
 
53
- def test_shuffled_circuits_with_readout_benchmarking_errors_no_noise():
54
- """Test shuffled circuits with readout benchmarking with no noise from sampler."""
55
- qubits = cirq.LineQubit.range(5)
56
+ def _create_test_circuits_with_sweep(
57
+ qubits: Sequence[cirq.Qid], n_circuits: int
58
+ ) -> tuple[list[cirq.Circuit], list[cirq.ParamResolver]]:
59
+ """Helper function to generate sweep circuits for testing."""
60
+ if len(qubits) < 2:
61
+ raise ValueError(
62
+ "Need at least two qubits to generate two-qubit circuits."
63
+ ) # pragma: no cover
64
+ theta_symbol = sympy.Symbol('theta')
65
+ phi_symbol = sympy.Symbol('phi')
56
66
 
57
- # Generate random input circuits
58
- input_circuits = _create_test_circuits(qubits, 3)
67
+ two_qubit_gates = [cirq.ISWAP, cirq.CNOT]
59
68
 
60
- sampler = cirq.Simulator()
61
- circuit_repetitions = 1
62
- # allow passing a seed
63
- rng = 123
64
- readout_repetitions = 1000
69
+ input_circuits = []
70
+ sweep_params: list[cirq.ParamResolver] = []
71
+ qubit_pairs = list(itertools.combinations(qubits, 2))
72
+ num_pairs = len(qubit_pairs)
73
+ for i in range(n_circuits):
74
+ gate = two_qubit_gates[i % len(two_qubit_gates)]
75
+ q0, q1 = qubit_pairs[i % num_pairs]
76
+ circuits = rqcg.generate_library_of_2q_circuits(
77
+ n_library_circuits=5, two_qubit_gate=gate, q0=q0, q1=q1
78
+ )
79
+ for circuit in circuits:
80
+ circuit += cirq.Circuit(cirq.X(q0) ** theta_symbol, cirq.Y(q1) ** phi_symbol)
81
+ circuit.append(cirq.measure(*qubits, key="m"))
82
+ sweep_params.append(cirq.ParamResolver({'theta': 0, 'phi': 1}))
83
+ input_circuits.extend(circuits)
84
+
85
+ return input_circuits, sweep_params
65
86
 
87
+
88
+ def _circuits_with_readout_benchmarking_errors_shuffled(
89
+ sampler: cirq.Sampler,
90
+ input_circuits: list[cirq.Circuit],
91
+ qubits: Sequence[cirq.Qid] | Sequence[Sequence[cirq.Qid]],
92
+ parameters: sc_readout.ReadoutBenchmarkingParams,
93
+ rng_or_seed: np.random.Generator | int,
94
+ ):
66
95
  measurements, readout_calibration_results = (
67
- cirq.contrib.shuffle_circuits.run_shuffled_with_readout_benchmarking(
68
- input_circuits,
69
- sampler,
70
- circuit_repetitions,
71
- rng,
72
- num_random_bitstrings=100,
73
- readout_repetitions=readout_repetitions,
96
+ sc_readout.run_shuffled_circuits_with_readout_benchmarking(
97
+ sampler, input_circuits, parameters, qubits, rng_or_seed
74
98
  )
75
99
  )
76
100
 
77
101
  for measurement in measurements:
78
102
  assert isinstance(measurement, ResultDict)
103
+ return readout_calibration_results
104
+
105
+
106
+ def _circuits_with_readout_benchmarking_errors_sweep(
107
+ sampler: cirq.Sampler,
108
+ input_circuits: list[cirq.Circuit],
109
+ qubits: Sequence[cirq.Qid] | Sequence[Sequence[cirq.Qid]],
110
+ sweep_params: list[cirq.ParamResolver],
111
+ parameters: sc_readout.ReadoutBenchmarkingParams,
112
+ rng_or_seed: np.random.Generator | int,
113
+ ):
114
+ sweep_measurements, readout_calibration_results = (
115
+ sc_readout.run_sweep_with_readout_benchmarking(
116
+ sampler, input_circuits, sweep_params, parameters, qubits, rng_or_seed
117
+ )
118
+ )
119
+
120
+ for measurement_group in sweep_measurements:
121
+ for single_sweep_measurement in measurement_group:
122
+ assert isinstance(single_sweep_measurement, ResultDict)
123
+ return readout_calibration_results
124
+
125
+
126
+ @pytest.mark.parametrize("mode", ["shuffled", "sweep"])
127
+ def test_circuits_with_readout_benchmarking_errors_no_noise(mode: str):
128
+ """Test shuffled/sweep circuits with readout benchmarking with no noise from sampler."""
129
+ qubits = cirq.LineQubit.range(5)
130
+
131
+ sampler = cirq.Simulator()
132
+ circuit_repetitions = 1
133
+ num_random_bitstrings = 100
134
+ readout_repetitions = 1000
135
+
136
+ readout_benchmarking_params = sc_readout.ReadoutBenchmarkingParams(
137
+ circuit_repetitions=circuit_repetitions,
138
+ num_random_bitstrings=num_random_bitstrings,
139
+ readout_repetitions=readout_repetitions,
140
+ )
141
+ # allow passing a seed
142
+ rng = 123
143
+
144
+ if mode == "shuffled":
145
+ input_circuits = _create_test_circuits(qubits, 3)
146
+ readout_calibration_results = _circuits_with_readout_benchmarking_errors_shuffled(
147
+ sampler, input_circuits, qubits, readout_benchmarking_params, rng
148
+ )
149
+
150
+ elif mode == "sweep":
151
+ input_circuits, sweep_params = _create_test_circuits_with_sweep(qubits, 3)
152
+
153
+ readout_calibration_results = _circuits_with_readout_benchmarking_errors_sweep(
154
+ sampler, input_circuits, qubits, sweep_params, readout_benchmarking_params, rng
155
+ )
79
156
 
80
157
  for qlist, readout_calibration_result in readout_calibration_results.items():
81
158
  assert isinstance(qlist, tuple)
@@ -88,31 +165,35 @@ def test_shuffled_circuits_with_readout_benchmarking_errors_no_noise():
88
165
  assert isinstance(readout_calibration_result.timestamp, float)
89
166
 
90
167
 
91
- def test_shuffled_circuits_with_readout_benchmarking_errors_with_noise():
92
- """Test shuffled circuits with readout benchmarking with noise from sampler."""
168
+ @pytest.mark.parametrize("mode", ["shuffled", "sweep"])
169
+ def test_circuits_with_readout_benchmarking_errors_with_noise(mode: str):
170
+ """Test shuffled/sweep circuits with readout benchmarking with noise from sampler."""
93
171
  qubits = cirq.LineQubit.range(6)
94
-
95
- # Generate random input circuits
96
- input_circuits = _create_test_circuits(qubits, 6)
97
-
98
172
  sampler = NoisySingleQubitReadoutSampler(p0=0.1, p1=0.2, seed=1234)
99
173
  circuit_repetitions = 1
100
174
  rng = np.random.default_rng()
101
175
  readout_repetitions = 1000
176
+ num_random_bitstrings = 100
102
177
 
103
- measurements, readout_calibration_results = (
104
- cirq.contrib.shuffle_circuits.run_shuffled_with_readout_benchmarking(
105
- input_circuits,
106
- sampler,
107
- circuit_repetitions,
108
- rng,
109
- num_random_bitstrings=100,
110
- readout_repetitions=readout_repetitions,
111
- )
178
+ readout_benchmarking_params = sc_readout.ReadoutBenchmarkingParams(
179
+ circuit_repetitions=circuit_repetitions,
180
+ num_random_bitstrings=num_random_bitstrings,
181
+ readout_repetitions=readout_repetitions,
112
182
  )
113
183
 
114
- for measurement in measurements:
115
- assert isinstance(measurement, ResultDict)
184
+ if mode == "shuffled":
185
+ input_circuits = _create_test_circuits(qubits, 6)
186
+
187
+ readout_calibration_results = _circuits_with_readout_benchmarking_errors_shuffled(
188
+ sampler, input_circuits, qubits, readout_benchmarking_params, rng
189
+ )
190
+
191
+ elif mode == "sweep":
192
+ input_circuits, sweep_params = _create_test_circuits_with_sweep(qubits, 6)
193
+
194
+ readout_calibration_results = _circuits_with_readout_benchmarking_errors_sweep(
195
+ sampler, input_circuits, qubits, sweep_params, readout_benchmarking_params, rng
196
+ )
116
197
 
117
198
  for qlist, readout_calibration_result in readout_calibration_results.items():
118
199
  assert isinstance(qlist, tuple)
@@ -127,33 +208,38 @@ def test_shuffled_circuits_with_readout_benchmarking_errors_with_noise():
127
208
  assert isinstance(readout_calibration_result.timestamp, float)
128
209
 
129
210
 
130
- def test_shuffled_circuits_with_readout_benchmarking_errors_with_noise_and_input_qubits():
131
- """Test shuffled circuits with readout benchmarking with noise from sampler and input qubits."""
211
+ @pytest.mark.parametrize("mode", ["shuffled", "sweep"])
212
+ def test_circuits_with_readout_benchmarking_errors_with_noise_and_input_qubits(mode: str):
213
+ """Test shuffled/sweep circuits with readout benchmarking with
214
+ noise from sampler and input qubits."""
132
215
  qubits = cirq.LineQubit.range(6)
133
216
  readout_qubits = qubits[:4]
134
217
 
135
- # Generate random input circuits
136
- input_circuits = _create_test_circuits(qubits, 6)
137
-
138
218
  sampler = NoisySingleQubitReadoutSampler(p0=0.1, p1=0.3, seed=1234)
139
219
  circuit_repetitions = 1
140
220
  rng = np.random.default_rng()
141
221
  readout_repetitions = 1000
222
+ num_random_bitstrings = 100
142
223
 
143
- measurements, readout_calibration_results = (
144
- cirq.contrib.shuffle_circuits.run_shuffled_with_readout_benchmarking(
145
- input_circuits,
146
- sampler,
147
- circuit_repetitions,
148
- rng,
149
- num_random_bitstrings=100,
150
- readout_repetitions=readout_repetitions,
151
- qubits=readout_qubits,
152
- )
224
+ readout_benchmarking_params = sc_readout.ReadoutBenchmarkingParams(
225
+ circuit_repetitions=circuit_repetitions,
226
+ num_random_bitstrings=num_random_bitstrings,
227
+ readout_repetitions=readout_repetitions,
153
228
  )
154
229
 
155
- for measurement in measurements:
156
- assert isinstance(measurement, ResultDict)
230
+ if mode == "shuffled":
231
+ input_circuits = _create_test_circuits(qubits, 6)
232
+
233
+ readout_calibration_results = _circuits_with_readout_benchmarking_errors_shuffled(
234
+ sampler, input_circuits, readout_qubits, readout_benchmarking_params, rng
235
+ )
236
+
237
+ elif mode == "sweep":
238
+ input_circuits, sweep_params = _create_test_circuits_with_sweep(qubits, 6)
239
+
240
+ readout_calibration_results = _circuits_with_readout_benchmarking_errors_sweep(
241
+ sampler, input_circuits, readout_qubits, sweep_params, readout_benchmarking_params, rng
242
+ )
157
243
 
158
244
  for qlist, readout_calibration_result in readout_calibration_results.items():
159
245
  assert isinstance(qlist, tuple)
@@ -168,35 +254,42 @@ def test_shuffled_circuits_with_readout_benchmarking_errors_with_noise_and_input
168
254
  assert isinstance(readout_calibration_result.timestamp, float)
169
255
 
170
256
 
171
- def test_shuffled_circuits_with_readout_benchmarking_errors_with_noise_and_lists_input_qubits():
172
- """Test shuffled circuits with readout benchmarking with noise from sampler and input qubits."""
257
+ @pytest.mark.parametrize("mode", ["shuffled", "sweep"])
258
+ def test_circuits_with_readout_benchmarking_errors_with_noise_and_lists_input_qubits(mode: str):
259
+ """Test shuffled/sweep circuits with readout benchmarking with noise
260
+ from sampler and input qubits."""
173
261
  qubits_1 = cirq.LineQubit.range(3)
174
262
  qubits_2 = cirq.LineQubit.range(4)
175
-
176
263
  readout_qubits = [qubits_1, qubits_2]
177
264
 
178
- # Generate random input circuits and append measurements
179
- input_circuits = _create_test_circuits(qubits_1, 6) + _create_test_circuits(qubits_2, 4)
180
-
181
265
  sampler = NoisySingleQubitReadoutSampler(p0=0.1, p1=0.3, seed=1234)
182
266
  circuit_repetitions = 1
183
267
  rng = np.random.default_rng()
184
268
  readout_repetitions = 1000
269
+ num_random_bitstrings = 100
185
270
 
186
- measurements, readout_calibration_results = (
187
- cirq.contrib.shuffle_circuits.run_shuffled_with_readout_benchmarking(
188
- input_circuits,
189
- sampler,
190
- circuit_repetitions,
191
- rng,
192
- num_random_bitstrings=100,
193
- readout_repetitions=readout_repetitions,
194
- qubits=readout_qubits,
195
- )
271
+ readout_benchmarking_params = sc_readout.ReadoutBenchmarkingParams(
272
+ circuit_repetitions=circuit_repetitions,
273
+ num_random_bitstrings=num_random_bitstrings,
274
+ readout_repetitions=readout_repetitions,
196
275
  )
197
276
 
198
- for measurement in measurements:
199
- assert isinstance(measurement, ResultDict)
277
+ if mode == "shuffled":
278
+ input_circuits = _create_test_circuits(qubits_1, 6) + _create_test_circuits(qubits_2, 4)
279
+
280
+ readout_calibration_results = _circuits_with_readout_benchmarking_errors_shuffled(
281
+ sampler, input_circuits, readout_qubits, readout_benchmarking_params, rng
282
+ )
283
+
284
+ elif mode == "sweep":
285
+ input_circuits, sweep_params = _create_test_circuits_with_sweep(qubits_1, 6)
286
+ additional_circuits, additional_sweep_params = _create_test_circuits_with_sweep(qubits_2, 4)
287
+ input_circuits += additional_circuits
288
+ sweep_params += additional_sweep_params
289
+
290
+ readout_calibration_results = _circuits_with_readout_benchmarking_errors_sweep(
291
+ sampler, input_circuits, readout_qubits, sweep_params, readout_benchmarking_params, rng
292
+ )
200
293
 
201
294
  for qlist, readout_calibration_result in readout_calibration_results.items():
202
295
  assert isinstance(qlist, tuple)
@@ -211,63 +304,198 @@ def test_shuffled_circuits_with_readout_benchmarking_errors_with_noise_and_lists
211
304
  assert isinstance(readout_calibration_result.timestamp, float)
212
305
 
213
306
 
214
- def test_can_handle_zero_random_bitstring():
215
- """Test shuffled circuits without readout benchmarking."""
307
+ @pytest.mark.parametrize("mode", ["shuffled", "sweep"])
308
+ def test_can_handle_zero_random_bitstring(mode: str):
309
+ """Test shuffled/sweep circuits without readout benchmarking."""
216
310
  qubits_1 = cirq.LineQubit.range(3)
217
311
  qubits_2 = cirq.LineQubit.range(4)
218
-
219
312
  readout_qubits = [qubits_1, qubits_2]
220
313
 
221
- # Generate random input circuits and append measurements
222
- input_circuits = _create_test_circuits(qubits_1, 6) + _create_test_circuits(qubits_2, 4)
223
-
224
314
  sampler = NoisySingleQubitReadoutSampler(p0=0.1, p1=0.3, seed=1234)
225
315
  circuit_repetitions = 1
226
316
  rng = np.random.default_rng()
227
317
  readout_repetitions = 1000
318
+ num_random_bitstrings = 0
228
319
 
229
- measurements, readout_calibration_results = (
230
- cirq.contrib.shuffle_circuits.run_shuffled_with_readout_benchmarking(
231
- input_circuits,
232
- sampler,
233
- circuit_repetitions,
234
- rng,
235
- num_random_bitstrings=0,
236
- readout_repetitions=readout_repetitions,
237
- qubits=readout_qubits,
238
- )
320
+ readout_benchmarking_params = sc_readout.ReadoutBenchmarkingParams(
321
+ circuit_repetitions=circuit_repetitions,
322
+ num_random_bitstrings=num_random_bitstrings,
323
+ readout_repetitions=readout_repetitions,
239
324
  )
240
325
 
241
- for measurement in measurements:
242
- assert isinstance(measurement, ResultDict)
326
+ if mode == "shuffled":
327
+ input_circuits = _create_test_circuits(qubits_1, 6) + _create_test_circuits(qubits_2, 4)
328
+
329
+ readout_calibration_results = _circuits_with_readout_benchmarking_errors_shuffled(
330
+ sampler, input_circuits, readout_qubits, readout_benchmarking_params, rng
331
+ )
332
+
333
+ elif mode == "sweep":
334
+ input_circuits, sweep_params = _create_test_circuits_with_sweep(qubits_1, 6)
335
+ additional_circuits, additional_sweep_params = _create_test_circuits_with_sweep(qubits_2, 4)
336
+ input_circuits += additional_circuits
337
+ sweep_params += additional_sweep_params
338
+
339
+ readout_calibration_results = _circuits_with_readout_benchmarking_errors_sweep(
340
+ sampler, input_circuits, readout_qubits, sweep_params, readout_benchmarking_params, rng
341
+ )
342
+
243
343
  # Check that the readout_calibration_results is empty
244
344
  assert len(readout_calibration_results.items()) == 0
245
345
 
246
346
 
347
+ @pytest.mark.parametrize("mode", ["shuffled", "sweep"])
348
+ def test_circuits_with_readout_benchmarking_no_qubits_arg_empty_rng(mode: str):
349
+ """Test benchmarking when the `qubits` argument is not provided."""
350
+ qubits = cirq.LineQubit.range(3)
351
+ sampler = NoisySingleQubitReadoutSampler(p0=0.1, p1=0.2, seed=1234)
352
+ circuit_repetitions = 1
353
+ readout_repetitions = 1000
354
+ num_random_bitstrings = 100
355
+
356
+ readout_benchmarking_params = sc_readout.ReadoutBenchmarkingParams(
357
+ circuit_repetitions=circuit_repetitions,
358
+ num_random_bitstrings=num_random_bitstrings,
359
+ readout_repetitions=readout_repetitions,
360
+ )
361
+
362
+ if mode == "shuffled":
363
+ input_circuits = _create_test_circuits(qubits, 3)
364
+ measurements, readout_calibration_results = (
365
+ sc_readout.run_shuffled_circuits_with_readout_benchmarking(
366
+ sampler, input_circuits, readout_benchmarking_params, None, None
367
+ )
368
+ )
369
+ assert len(measurements) == len(input_circuits)
370
+ else: # mode == "sweep"
371
+ input_circuits, sweep_params = _create_test_circuits_with_sweep(qubits, 3)
372
+ sweep_measurements, readout_calibration_results = (
373
+ sc_readout.run_sweep_with_readout_benchmarking(
374
+ sampler, input_circuits, sweep_params, readout_benchmarking_params, None, None
375
+ )
376
+ )
377
+ assert len(sweep_measurements) == len(input_circuits)
378
+
379
+ # When qubits is None, all qubits from input circuits are benchmarked as one group.
380
+ assert len(readout_calibration_results) == 1
381
+ qlist, result = list(readout_calibration_results.items())[0]
382
+ assert isinstance(qlist, tuple)
383
+ assert set(qlist) == set(qubits)
384
+ assert isinstance(result, SingleQubitReadoutCalibrationResult)
385
+ for error in result.zero_state_errors.values():
386
+ assert 0.08 < error < 0.12
387
+ for error in result.one_state_errors.values():
388
+ assert 0.18 < error < 0.22
389
+ assert result.repetitions == readout_repetitions
390
+
391
+
392
+ def test_deprecated_run_shuffled_with_readout_benchmarking():
393
+ """Test that the deprecated function works correctly and is covered."""
394
+ qubits = cirq.LineQubit.range(3)
395
+ input_circuits = _create_test_circuits(qubits, 3)
396
+ sampler = NoisySingleQubitReadoutSampler(p0=0.1, p1=0.2, seed=1234)
397
+ circuit_repetitions = 1
398
+ readout_repetitions = 1000
399
+ num_random_bitstrings = 100
400
+
401
+ # Test with an integer seed.
402
+ with cirq.testing.assert_deprecated(deadline='v2.0', count=1):
403
+ measurements_seed, results_seed = sc_readout.run_shuffled_with_readout_benchmarking(
404
+ input_circuits=input_circuits,
405
+ sampler=sampler,
406
+ circuit_repetitions=circuit_repetitions,
407
+ rng_or_seed=123,
408
+ num_random_bitstrings=num_random_bitstrings,
409
+ readout_repetitions=readout_repetitions,
410
+ qubits=qubits,
411
+ )
412
+ assert len(measurements_seed) == len(input_circuits)
413
+ qlist, result = list(results_seed.items())[0]
414
+ assert tuple(qubits) == qlist
415
+ for error in result.zero_state_errors.values():
416
+ assert 0.08 < error < 0.12
417
+ for error in result.one_state_errors.values():
418
+ assert 0.18 < error < 0.22
419
+
420
+ # Test with qubits=None to cover the auto-detection branch.
421
+ with cirq.testing.assert_deprecated(deadline='v2.0', count=1):
422
+ _, results_none = sc_readout.run_shuffled_with_readout_benchmarking(
423
+ input_circuits=input_circuits,
424
+ sampler=sampler,
425
+ circuit_repetitions=circuit_repetitions,
426
+ rng_or_seed=123,
427
+ num_random_bitstrings=num_random_bitstrings,
428
+ readout_repetitions=readout_repetitions,
429
+ qubits=None,
430
+ )
431
+ qlist_none, _ = list(results_none.items())[0]
432
+ assert set(qlist_none) == set(qubits)
433
+
434
+ # Test circuit_repetitions must be > 0
435
+ with cirq.testing.assert_deprecated(deadline="v2.0", count=1):
436
+ with pytest.raises(ValueError, match="Must provide non-zero circuit_repetitions."):
437
+ sc_readout.run_shuffled_with_readout_benchmarking(
438
+ input_circuits,
439
+ sampler,
440
+ circuit_repetitions=0,
441
+ num_random_bitstrings=5,
442
+ readout_repetitions=100,
443
+ rng_or_seed=123,
444
+ )
445
+
446
+ # Test num_random_bitstrings must be >= 0
447
+ with cirq.testing.assert_deprecated(deadline="v2.0", count=1):
448
+ with pytest.raises(ValueError, match="Must provide zero or more num_random_bitstrings."):
449
+ sc_readout.run_shuffled_with_readout_benchmarking(
450
+ input_circuits,
451
+ sampler,
452
+ circuit_repetitions=10,
453
+ num_random_bitstrings=-1,
454
+ readout_repetitions=100,
455
+ rng_or_seed=123,
456
+ )
457
+
458
+ # Test readout_repetitions must be > 0
459
+ with cirq.testing.assert_deprecated(deadline="v2.0", count=1):
460
+ with pytest.raises(
461
+ ValueError, match="Must provide non-zero readout_repetitions for readout calibration."
462
+ ):
463
+ sc_readout.run_shuffled_with_readout_benchmarking(
464
+ input_circuits,
465
+ sampler,
466
+ circuit_repetitions=10,
467
+ num_random_bitstrings=1,
468
+ readout_repetitions=0,
469
+ rng_or_seed=123,
470
+ )
471
+
472
+
247
473
  def test_empty_input_circuits():
248
474
  """Test that the input circuits are empty."""
475
+ readout_benchmarking_params = sc_readout.ReadoutBenchmarkingParams(
476
+ circuit_repetitions=10, num_random_bitstrings=5, readout_repetitions=100
477
+ )
249
478
  with pytest.raises(ValueError, match="Input circuits must not be empty."):
250
- cirq.contrib.shuffle_circuits.run_shuffled_with_readout_benchmarking(
251
- [],
479
+ sc_readout.run_shuffled_circuits_with_readout_benchmarking(
252
480
  cirq.ZerosSampler(),
253
- circuit_repetitions=10,
481
+ [],
482
+ readout_benchmarking_params,
254
483
  rng_or_seed=np.random.default_rng(456),
255
- num_random_bitstrings=5,
256
- readout_repetitions=100,
257
484
  )
258
485
 
259
486
 
260
487
  def test_non_circuit_input():
261
488
  """Test that the input circuits are not of type cirq.Circuit."""
262
489
  q = cirq.LineQubit(0)
490
+ readout_benchmarking_params = sc_readout.ReadoutBenchmarkingParams(
491
+ circuit_repetitions=10, num_random_bitstrings=5, readout_repetitions=100
492
+ )
263
493
  with pytest.raises(ValueError, match="Input circuits must be of type cirq.Circuit."):
264
- cirq.contrib.shuffle_circuits.run_shuffled_with_readout_benchmarking(
265
- [q],
494
+ sc_readout.run_shuffled_circuits_with_readout_benchmarking(
266
495
  cirq.ZerosSampler(),
267
- circuit_repetitions=10,
496
+ [q],
497
+ readout_benchmarking_params,
268
498
  rng_or_seed=np.random.default_rng(456),
269
- num_random_bitstrings=5,
270
- readout_repetitions=100,
271
499
  )
272
500
 
273
501
 
@@ -275,29 +503,23 @@ def test_no_measurements():
275
503
  """Test that the input circuits don't have measurements."""
276
504
  q = cirq.LineQubit(0)
277
505
  circuit = cirq.Circuit(cirq.H(q))
506
+ readout_benchmarking_params = sc_readout.ReadoutBenchmarkingParams(
507
+ circuit_repetitions=10, num_random_bitstrings=5, readout_repetitions=100
508
+ )
278
509
  with pytest.raises(ValueError, match="Input circuits must have measurements."):
279
- cirq.contrib.shuffle_circuits.run_shuffled_with_readout_benchmarking(
280
- [circuit],
510
+ sc_readout.run_shuffled_circuits_with_readout_benchmarking(
281
511
  cirq.ZerosSampler(),
282
- circuit_repetitions=10,
512
+ [circuit],
513
+ readout_benchmarking_params,
283
514
  rng_or_seed=np.random.default_rng(456),
284
- num_random_bitstrings=5,
285
- readout_repetitions=100,
286
515
  )
287
516
 
288
517
 
289
518
  def test_zero_circuit_repetitions():
290
519
  """Test that the circuit repetitions are zero."""
291
- q = cirq.LineQubit(0)
292
- circuit = cirq.Circuit(cirq.H(q), cirq.measure(q))
293
520
  with pytest.raises(ValueError, match="Must provide non-zero circuit_repetitions."):
294
- cirq.contrib.shuffle_circuits.run_shuffled_with_readout_benchmarking(
295
- [circuit],
296
- cirq.ZerosSampler(),
297
- circuit_repetitions=0,
298
- rng_or_seed=np.random.default_rng(456),
299
- num_random_bitstrings=5,
300
- readout_repetitions=100,
521
+ sc_readout.ReadoutBenchmarkingParams(
522
+ circuit_repetitions=0, num_random_bitstrings=5, readout_repetitions=100
301
523
  )
302
524
 
303
525
 
@@ -305,62 +527,51 @@ def test_mismatch_circuit_repetitions():
305
527
  """Test that the number of circuit repetitions don't match the number of input circuits."""
306
528
  q = cirq.LineQubit(0)
307
529
  circuit = cirq.Circuit(cirq.H(q), cirq.measure(q))
530
+ readout_benchmarking_params = sc_readout.ReadoutBenchmarkingParams(
531
+ circuit_repetitions=[10, 20], num_random_bitstrings=5, readout_repetitions=100
532
+ )
308
533
  with pytest.raises(
309
534
  ValueError,
310
535
  match="Number of circuit_repetitions must match the number of" + " input circuits.",
311
536
  ):
312
- cirq.contrib.shuffle_circuits.run_shuffled_with_readout_benchmarking(
313
- [circuit],
537
+ sc_readout.run_shuffled_circuits_with_readout_benchmarking(
314
538
  cirq.ZerosSampler(),
315
- circuit_repetitions=[10, 20],
539
+ [circuit],
540
+ readout_benchmarking_params,
316
541
  rng_or_seed=np.random.default_rng(456),
317
- num_random_bitstrings=5,
318
- readout_repetitions=100,
319
542
  )
320
543
 
321
544
 
322
545
  def test_zero_num_random_bitstrings():
323
546
  """Test that the number of random bitstrings is smaller than zero."""
324
- q = cirq.LineQubit(0)
325
- circuit = cirq.Circuit(cirq.H(q), cirq.measure(q))
326
547
  with pytest.raises(ValueError, match="Must provide zero or more num_random_bitstrings."):
327
- cirq.contrib.shuffle_circuits.run_shuffled_with_readout_benchmarking(
328
- [circuit],
329
- cirq.ZerosSampler(),
330
- circuit_repetitions=10,
331
- rng_or_seed=np.random.default_rng(456),
332
- num_random_bitstrings=-1,
333
- readout_repetitions=100,
548
+ sc_readout.ReadoutBenchmarkingParams(
549
+ circuit_repetitions=10, num_random_bitstrings=-1, readout_repetitions=100
334
550
  )
335
551
 
336
552
 
337
553
  def test_zero_readout_repetitions():
338
554
  """Test that the readout repetitions is zero."""
339
- q = cirq.LineQubit(0)
340
- circuit = cirq.Circuit(cirq.H(q), cirq.measure(q))
341
555
  with pytest.raises(
342
556
  ValueError, match="Must provide non-zero readout_repetitions for readout" + " calibration."
343
557
  ):
344
- cirq.contrib.shuffle_circuits.run_shuffled_with_readout_benchmarking(
345
- [circuit],
346
- cirq.ZerosSampler(),
347
- circuit_repetitions=10,
348
- rng_or_seed=np.random.default_rng(456),
349
- num_random_bitstrings=5,
350
- readout_repetitions=0,
558
+ sc_readout.ReadoutBenchmarkingParams(
559
+ circuit_repetitions=10, num_random_bitstrings=5, readout_repetitions=0
351
560
  )
352
561
 
353
562
 
354
- def test_rng_type_mismatch():
355
- """Test that the rng is not a numpy random generator or a seed."""
356
- q = cirq.LineQubit(0)
357
- circuit = cirq.Circuit(cirq.H(q), cirq.measure(q))
358
- with pytest.raises(ValueError, match="Must provide a numpy random generator or a seed"):
359
- cirq.contrib.shuffle_circuits.run_shuffled_with_readout_benchmarking(
360
- [circuit],
563
+ def test_empty_sweep_params():
564
+ """Test that the sweep params are empty."""
565
+ q = cirq.LineQubit(5)
566
+ circuit = cirq.Circuit(cirq.H(q))
567
+ readout_benchmarking_params = sc_readout.ReadoutBenchmarkingParams(
568
+ circuit_repetitions=10, num_random_bitstrings=5, readout_repetitions=100
569
+ )
570
+ with pytest.raises(ValueError, match="Sweep parameters must not be empty."):
571
+ sc_readout.run_sweep_with_readout_benchmarking(
361
572
  cirq.ZerosSampler(),
362
- circuit_repetitions=10,
363
- rng_or_seed="not a random generator or seed",
364
- num_random_bitstrings=5,
365
- readout_repetitions=100,
573
+ [circuit],
574
+ [],
575
+ readout_benchmarking_params,
576
+ rng_or_seed=np.random.default_rng(456),
366
577
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cirq-core
3
- Version: 1.7.0.dev20250801234137
3
+ Version: 1.7.0.dev20250802153528
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
@@ -4,8 +4,8 @@ cirq/_compat_test.py,sha256=emXpdD5ZvwLRlFAoQB8YatmZyU3b4e9jg6FppMTUhkU,33900
4
4
  cirq/_doc.py,sha256=BrnoABo1hk5RgB3Cgww4zLHUfiyFny0F1V-tOMCbdaU,2909
5
5
  cirq/_import.py,sha256=ixBu4EyGl46Ram2cP3p5eZVEFDW5L2DS-VyTjz4N9iw,8429
6
6
  cirq/_import_test.py,sha256=oF4izzOVZLc7NZ0aZHFcGv-r01eiFFt_JORx_x7_D4s,1089
7
- cirq/_version.py,sha256=v-Y8oWV6-CO8-XIifuQbdv1876E-DUqEseper6SVvIQ,1206
8
- cirq/_version_test.py,sha256=xpPRdpMNi2Qgiba_8qsurRpvFM0K2k0jy1v2WFkV-us,155
7
+ cirq/_version.py,sha256=wSQKQOGopvZJRhsKDpY5aSkv1Y2mhfD8zASRrZPCG1Y,1206
8
+ cirq/_version_test.py,sha256=ZM2h_c5Uep4btUELQpxpHmql8B29Dn2jScx01mZqbjI,155
9
9
  cirq/conftest.py,sha256=wSDKNdIQRDfLnXvOCWD3erheOw8JHRhdfQ53EyTUIXg,1239
10
10
  cirq/json_resolver_cache.py,sha256=A5DIgFAY1hUNt9vai_C3-gGBv24116CJMzQxMcXOax4,13726
11
11
  cirq/py.typed,sha256=VFSlmh_lNwnaXzwY-ZuW-C2Ws5PkuDoVgBdNCs0jXJE,63
@@ -108,8 +108,8 @@ cirq/contrib/paulistring/optimize.py,sha256=F02c_9nuc8a41XNsA9bzTGaW2kR3hZw-MdaQ
108
108
  cirq/contrib/paulistring/optimize_test.py,sha256=FsmwyYFIGyyiO115oYgmCfaSV3De55Azd0_rzsi_xnU,3618
109
109
  cirq/contrib/paulistring/pauli_string_dag.py,sha256=28bUVNsIS9WYKdyYCNIVrkRwqQOKlkpmCacWow6N6D0,1142
110
110
  cirq/contrib/paulistring/pauli_string_dag_test.py,sha256=nH_1h5LQobV9rb5gitLmrvpIwWwrcRmNdUGDAhFMZtI,1168
111
- cirq/contrib/paulistring/pauli_string_measurement_with_readout_mitigation.py,sha256=otBl5RV6Xsvv1M2Ze6mSz7_wSzsvWt_K7kDWbJNqXYg,21087
112
- cirq/contrib/paulistring/pauli_string_measurement_with_readout_mitigation_test.py,sha256=tWJtEJwT2HgDj4vzREMymsPNKZlWLIFMbQ0qyEuVQpo,36829
111
+ cirq/contrib/paulistring/pauli_string_measurement_with_readout_mitigation.py,sha256=xrG4KNJaJjjhCG_0KvnOFdAOTbNRGJhYwyhCSBjWkWw,21288
112
+ cirq/contrib/paulistring/pauli_string_measurement_with_readout_mitigation_test.py,sha256=fgHEXhVBC1kaxkf-oCBcG62u1Ysox-o76TaXYC0-0X4,36803
113
113
  cirq/contrib/paulistring/pauli_string_optimize.py,sha256=ejHf7Bo0iUvnNBeZ5IN0bT0SIXF79DSGr1NxoAyVfiQ,2960
114
114
  cirq/contrib/paulistring/pauli_string_optimize_test.py,sha256=_14FS9TAvzRsmnTZxJUsMXPNcenv5mb0eD2gGTxvohE,2955
115
115
  cirq/contrib/paulistring/recombine.py,sha256=phJ-SY4zdqZpIZca0iSsY0lK6NdXd0M0sOOWnUdGn5U,4353
@@ -161,9 +161,9 @@ cirq/contrib/routing/swap_network.py,sha256=wQPDb3ZyIyaB1O2H7IyoZxfsGsuVn5GpdpDS
161
161
  cirq/contrib/routing/swap_network_test.py,sha256=3xahmqi5etiksFgD1aNIqp_KnQ4JuhHeHF46h4FMGwA,4866
162
162
  cirq/contrib/routing/utils.py,sha256=xvA1khTMBly750GVJm_pCc5uBpAHpayLGZ-Yq4m2qg8,3780
163
163
  cirq/contrib/routing/utils_test.py,sha256=4ssy2pXdHKRv99on91ag1SgZihYEfNR96i4AuTh90nM,2057
164
- cirq/contrib/shuffle_circuits/__init__.py,sha256=AL-V3OaZiaF596WTLlyxDPk0t1WMpTHpQrpRW_A9t48,832
165
- cirq/contrib/shuffle_circuits/shuffle_circuits_with_readout_benchmarking.py,sha256=D-lsr1y65fiDtuPJ4wWPy3LrnB4w6hVVV6LT6cuMZU0,10851
166
- cirq/contrib/shuffle_circuits/shuffle_circuits_with_readout_benchmarking_test.py,sha256=ZtJpaXOeFG2PgywULlybSf8LZ24yT4nh2oiMepDXKxg,13884
164
+ cirq/contrib/shuffle_circuits/__init__.py,sha256=yKqR59wvZYmIrolsEEqoO9Dhgd5hALMkAoR9AtjnbUY,1030
165
+ cirq/contrib/shuffle_circuits/shuffle_circuits_with_readout_benchmarking.py,sha256=QOiZeswqkGuuzIpFjSUID1-Y8H0lm5vhLW_poFzWa6E,22764
166
+ cirq/contrib/shuffle_circuits/shuffle_circuits_with_readout_benchmarking_test.py,sha256=vk9wjIQwaOviqnzEnG-2IT8cLMLR4xEsP3EXHeeqLqs,23770
167
167
  cirq/contrib/svg/__init__.py,sha256=m7d-CNT2j74uNQdmM2xJ1a7HG6v0FZMt8eAwW4rPJpI,148
168
168
  cirq/contrib/svg/svg.py,sha256=TU273Ei6XhKr_pbHyQPx2BPa-59r6L70IzDT8LvgDMQ,9391
169
169
  cirq/contrib/svg/svg_test.py,sha256=YYgUxcthavh4cTbTcb3NIabJVCi0u2AOeyF7rJWicQ4,2507
@@ -1234,8 +1234,8 @@ cirq/work/sampler.py,sha256=rxbMWvrhu3gfNSBjZKozw28lLKVvBAS_1EGyPdYe8Xg,19041
1234
1234
  cirq/work/sampler_test.py,sha256=SsMrRvLDYELyOAWLKISjkdEfrBwLYWRsT6D8WrsLM3Q,13533
1235
1235
  cirq/work/zeros_sampler.py,sha256=Fs2JWwq0n9zv7_G5Rm-9vPeHUag7uctcMOHg0JTkZpc,2371
1236
1236
  cirq/work/zeros_sampler_test.py,sha256=lQLgQDGBLtfImryys2HzQ2jOSGxHgc7-koVBUhv8qYk,3345
1237
- cirq_core-1.7.0.dev20250801234137.dist-info/licenses/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
1238
- cirq_core-1.7.0.dev20250801234137.dist-info/METADATA,sha256=ndhwsaztrDCmKbcCXlvGkWmJCkSmiS6Ebdl4KHVbVEs,4857
1239
- cirq_core-1.7.0.dev20250801234137.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
1240
- cirq_core-1.7.0.dev20250801234137.dist-info/top_level.txt,sha256=Sz9iOxHU0IEMLSFGwiwOCaN2e9K-jFbBbtpPN1hB73g,5
1241
- cirq_core-1.7.0.dev20250801234137.dist-info/RECORD,,
1237
+ cirq_core-1.7.0.dev20250802153528.dist-info/licenses/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
1238
+ cirq_core-1.7.0.dev20250802153528.dist-info/METADATA,sha256=RF6o2bdnzCefg7ACILSjFXUbyyJfSY82vNVNOuLEOHY,4857
1239
+ cirq_core-1.7.0.dev20250802153528.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
1240
+ cirq_core-1.7.0.dev20250802153528.dist-info/top_level.txt,sha256=Sz9iOxHU0IEMLSFGwiwOCaN2e9K-jFbBbtpPN1hB73g,5
1241
+ cirq_core-1.7.0.dev20250802153528.dist-info/RECORD,,