cirq-core 1.3.0.dev20231201141002__py3-none-any.whl → 1.4.0__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.
Files changed (157) hide show
  1. cirq/__init__.py +4 -0
  2. cirq/_compat.py +9 -11
  3. cirq/_compat_test.py +45 -56
  4. cirq/_version.py +31 -1
  5. cirq/_version_test.py +1 -1
  6. cirq/circuits/circuit.py +13 -8
  7. cirq/circuits/circuit_operation.py +2 -1
  8. cirq/circuits/circuit_test.py +2 -2
  9. cirq/circuits/frozen_circuit.py +3 -2
  10. cirq/circuits/moment.py +12 -10
  11. cirq/circuits/qasm_output.py +5 -1
  12. cirq/circuits/qasm_output_test.py +25 -10
  13. cirq/contrib/qcircuit/qcircuit_diagram_info.py +9 -7
  14. cirq/contrib/quimb/mps_simulator_test.py +1 -1
  15. cirq/contrib/quimb/state_vector.py +9 -2
  16. cirq/contrib/svg/svg.py +2 -1
  17. cirq/contrib/svg/svg_test.py +1 -0
  18. cirq/devices/grid_qubit.py +85 -32
  19. cirq/devices/grid_qubit_test.py +22 -4
  20. cirq/devices/line_qubit.py +74 -26
  21. cirq/devices/line_qubit_test.py +19 -0
  22. cirq/devices/noise_utils.py +33 -31
  23. cirq/devices/noise_utils_test.py +1 -84
  24. cirq/devices/superconducting_qubits_noise_properties.py +7 -6
  25. cirq/experiments/__init__.py +8 -0
  26. cirq/experiments/qubit_characterizations.py +288 -44
  27. cirq/experiments/qubit_characterizations_test.py +61 -7
  28. cirq/experiments/random_quantum_circuit_generation.py +1 -1
  29. cirq/experiments/single_qubit_readout_calibration.py +132 -6
  30. cirq/experiments/single_qubit_readout_calibration_test.py +3 -1
  31. cirq/experiments/t1_decay_experiment.py +14 -7
  32. cirq/experiments/t1_decay_experiment_test.py +14 -26
  33. cirq/experiments/two_qubit_xeb.py +483 -0
  34. cirq/experiments/two_qubit_xeb_test.py +304 -0
  35. cirq/json_resolver_cache.py +2 -0
  36. cirq/linalg/decompositions.py +11 -13
  37. cirq/linalg/decompositions_test.py +1 -3
  38. cirq/linalg/diagonalize.py +5 -4
  39. cirq/linalg/predicates.py +8 -6
  40. cirq/linalg/transformations.py +2 -1
  41. cirq/linalg/transformations_test.py +1 -1
  42. cirq/ops/__init__.py +2 -0
  43. cirq/ops/clifford_gate.py +59 -16
  44. cirq/ops/common_gates_test.py +1 -2
  45. cirq/ops/control_values.py +4 -3
  46. cirq/ops/controlled_gate_test.py +1 -3
  47. cirq/ops/gate_operation.py +10 -1
  48. cirq/ops/named_qubit.py +74 -28
  49. cirq/ops/named_qubit_test.py +19 -0
  50. cirq/ops/parity_gates.py +5 -0
  51. cirq/ops/parity_gates_test.py +2 -10
  52. cirq/ops/pauli_gates.py +5 -2
  53. cirq/ops/pauli_string.py +2 -2
  54. cirq/ops/permutation_gate.py +16 -18
  55. cirq/ops/phased_iswap_gate_test.py +1 -3
  56. cirq/ops/phased_x_gate.py +1 -1
  57. cirq/ops/phased_x_z_gate.py +17 -1
  58. cirq/ops/phased_x_z_gate_test.py +24 -0
  59. cirq/ops/qid_util.py +4 -8
  60. cirq/ops/qubit_manager.py +7 -4
  61. cirq/ops/qubit_manager_test.py +20 -0
  62. cirq/ops/raw_types.py +5 -2
  63. cirq/ops/raw_types_test.py +14 -15
  64. cirq/ops/uniform_superposition_gate.py +123 -0
  65. cirq/ops/uniform_superposition_gate_test.py +94 -0
  66. cirq/protocols/approximate_equality_protocol_test.py +2 -2
  67. cirq/protocols/circuit_diagram_info_protocol.py +6 -4
  68. cirq/protocols/commutes_protocol.py +2 -4
  69. cirq/protocols/decompose_protocol.py +7 -12
  70. cirq/protocols/decompose_protocol_test.py +7 -3
  71. cirq/protocols/has_stabilizer_effect_protocol.py +1 -5
  72. cirq/protocols/has_stabilizer_effect_protocol_test.py +13 -4
  73. cirq/protocols/json_serialization.py +51 -181
  74. cirq/protocols/json_serialization_test.py +13 -47
  75. cirq/protocols/json_test_data/CircuitOperation.json +131 -148
  76. cirq/protocols/json_test_data/CircuitOperation.json_inward +55 -0
  77. cirq/protocols/json_test_data/CircuitOperation.repr_inward +6 -0
  78. cirq/protocols/json_test_data/FrozenCircuit.json +196 -210
  79. cirq/protocols/json_test_data/FrozenCircuit.json_inward +35 -0
  80. cirq/protocols/json_test_data/FrozenCircuit.repr_inward +4 -0
  81. cirq/protocols/json_test_data/UniformSuperpositionGate.json +5 -0
  82. cirq/protocols/json_test_data/UniformSuperpositionGate.repr +1 -0
  83. cirq/protocols/json_test_data/cirq.MSGate.json +4 -0
  84. cirq/protocols/json_test_data/cirq.MSGate.repr +1 -0
  85. cirq/protocols/json_test_data/spec.py +2 -0
  86. cirq/protocols/pow_protocol_test.py +1 -3
  87. cirq/protocols/resolve_parameters.py +4 -2
  88. cirq/qis/__init__.py +10 -0
  89. cirq/qis/clifford_tableau.py +8 -2
  90. cirq/qis/noise_utils.py +123 -0
  91. cirq/qis/noise_utils_test.py +97 -0
  92. cirq/sim/classical_simulator.py +227 -87
  93. cirq/sim/classical_simulator_test.py +135 -0
  94. cirq/sim/clifford/clifford_simulator_test.py +4 -2
  95. cirq/sim/mux.py +5 -3
  96. cirq/sim/simulation_product_state.py +15 -10
  97. cirq/sim/simulation_state.py +1 -1
  98. cirq/sim/simulation_state_test.py +2 -2
  99. cirq/sim/simulator_base.py +3 -3
  100. cirq/sim/state_vector_simulation_state.py +4 -4
  101. cirq/sim/state_vector_simulator.py +17 -2
  102. cirq/study/__init__.py +1 -0
  103. cirq/study/result.py +14 -0
  104. cirq/study/result_test.py +6 -0
  105. cirq/study/sweeps.py +4 -2
  106. cirq/study/sweeps_test.py +8 -0
  107. cirq/testing/__init__.py +6 -1
  108. cirq/testing/_compat_test_data/__init__.py +3 -3
  109. cirq/testing/_compat_test_data/module_a/__init__.py +2 -2
  110. cirq/testing/circuit_compare.py +1 -1
  111. cirq/testing/consistent_qasm.py +6 -0
  112. cirq/testing/gate_features.py +10 -0
  113. cirq/testing/lin_alg_utils.py +5 -3
  114. cirq/transformers/__init__.py +15 -0
  115. cirq/transformers/analytical_decompositions/controlled_gate_decomposition.py +3 -1
  116. cirq/transformers/analytical_decompositions/two_qubit_to_cz.py +24 -0
  117. cirq/transformers/analytical_decompositions/two_qubit_to_cz_test.py +17 -0
  118. cirq/transformers/dynamical_decoupling.py +122 -0
  119. cirq/transformers/dynamical_decoupling_test.py +123 -0
  120. cirq/transformers/gauge_compiling/__init__.py +26 -0
  121. cirq/transformers/gauge_compiling/cz_gauge.py +46 -0
  122. cirq/transformers/gauge_compiling/cz_gauge_test.py +23 -0
  123. cirq/transformers/gauge_compiling/gauge_compiling.py +214 -0
  124. cirq/transformers/gauge_compiling/gauge_compiling_test.py +41 -0
  125. cirq/transformers/gauge_compiling/gauge_compiling_test_utils.py +83 -0
  126. cirq/transformers/gauge_compiling/gauge_compiling_test_utils_test.py +52 -0
  127. cirq/transformers/gauge_compiling/iswap_gauge.py +105 -0
  128. cirq/transformers/gauge_compiling/iswap_gauge_test.py +23 -0
  129. cirq/transformers/gauge_compiling/spin_inversion_gauge.py +33 -0
  130. cirq/transformers/gauge_compiling/spin_inversion_gauge_test.py +37 -0
  131. cirq/transformers/gauge_compiling/sqrt_cz_gauge.py +64 -0
  132. cirq/transformers/gauge_compiling/sqrt_cz_gauge_test.py +27 -0
  133. cirq/transformers/gauge_compiling/sqrt_iswap_gauge.py +94 -0
  134. cirq/transformers/gauge_compiling/sqrt_iswap_gauge_test.py +22 -0
  135. cirq/transformers/heuristic_decompositions/two_qubit_gate_tabulation.py +1 -0
  136. cirq/transformers/merge_k_qubit_gates_test.py +23 -23
  137. cirq/transformers/merge_single_qubit_gates_test.py +14 -14
  138. cirq/transformers/optimize_for_target_gateset.py +39 -17
  139. cirq/transformers/optimize_for_target_gateset_test.py +189 -39
  140. cirq/transformers/qubit_management_transformers.py +1 -1
  141. cirq/transformers/routing/visualize_routed_circuit_test.py +17 -17
  142. cirq/transformers/stratify_test.py +13 -13
  143. cirq/transformers/target_gatesets/compilation_target_gateset.py +26 -2
  144. cirq/transformers/target_gatesets/compilation_target_gateset_test.py +16 -16
  145. cirq/transformers/target_gatesets/cz_gateset.py +4 -0
  146. cirq/transformers/transformer_api.py +1 -2
  147. cirq/transformers/transformer_primitives.py +15 -14
  148. cirq/transformers/transformer_primitives_test.py +99 -72
  149. cirq/value/classical_data.py +6 -6
  150. cirq/value/value_equality_attr.py +4 -0
  151. cirq/work/sampler.py +3 -4
  152. cirq/work/sampler_test.py +25 -0
  153. {cirq_core-1.3.0.dev20231201141002.dist-info → cirq_core-1.4.0.dist-info}/METADATA +10 -19
  154. {cirq_core-1.3.0.dev20231201141002.dist-info → cirq_core-1.4.0.dist-info}/RECORD +157 -130
  155. {cirq_core-1.3.0.dev20231201141002.dist-info → cirq_core-1.4.0.dist-info}/WHEEL +1 -1
  156. {cirq_core-1.3.0.dev20231201141002.dist-info → cirq_core-1.4.0.dist-info}/LICENSE +0 -0
  157. {cirq_core-1.3.0.dev20231201141002.dist-info → cirq_core-1.4.0.dist-info}/top_level.txt +0 -0
@@ -17,9 +17,10 @@
17
17
 
18
18
  import abc
19
19
  from dataclasses import dataclass, field
20
+ from functools import cached_property
20
21
  from typing import Dict, TYPE_CHECKING, List, Set, Type
21
22
 
22
- from cirq import _compat, ops, devices
23
+ from cirq import ops, devices, qis
23
24
  from cirq.devices import noise_utils
24
25
 
25
26
  if TYPE_CHECKING:
@@ -128,10 +129,10 @@ class SuperconductingQubitsNoiseProperties(devices.NoiseProperties, abc.ABC):
128
129
  def _get_pauli_error(self, p_error: float, op_id: noise_utils.OpIdentifier):
129
130
  time_ns = float(self.gate_times_ns[op_id.gate_type])
130
131
  for q in op_id.qubits:
131
- p_error -= noise_utils.decoherence_pauli_error(self.t1_ns[q], self.tphi_ns[q], time_ns)
132
+ p_error -= qis.decoherence_pauli_error(self.t1_ns[q], self.tphi_ns[q], time_ns)
132
133
  return p_error
133
134
 
134
- @_compat.cached_property
135
+ @cached_property
135
136
  def _depolarizing_error(self) -> Dict[noise_utils.OpIdentifier, float]:
136
137
  """Returns the portion of Pauli error from depolarization."""
137
138
  depol_errors = {}
@@ -179,9 +180,9 @@ class SuperconductingQubitsNoiseProperties(devices.NoiseProperties, abc.ABC):
179
180
  p_00, p_11 = self.readout_errors[qubit]
180
181
  p = p_11 / (p_00 + p_11)
181
182
  gamma = p_11 / p
182
- added_measure_errors[
183
- noise_utils.OpIdentifier(ops.MeasurementGate, qubit)
184
- ] = ops.generalized_amplitude_damp(p, gamma).on(qubit)
183
+ added_measure_errors[noise_utils.OpIdentifier(ops.MeasurementGate, qubit)] = (
184
+ ops.generalized_amplitude_damp(p, gamma).on(qubit)
185
+ )
185
186
 
186
187
  noise_models.append(
187
188
  devices.InsertionNoiseModel(ops_added=added_measure_errors, prepend=True)
@@ -20,6 +20,7 @@ from cirq.experiments.qubit_characterizations import (
20
20
  TomographyResult,
21
21
  two_qubit_randomized_benchmarking,
22
22
  two_qubit_state_tomography,
23
+ parallel_single_qubit_randomized_benchmarking,
23
24
  )
24
25
 
25
26
  from cirq.experiments.fidelity_estimation import (
@@ -63,3 +64,10 @@ from cirq.experiments.t1_decay_experiment import t1_decay, T1DecayResult
63
64
  from cirq.experiments.t2_decay_experiment import t2_decay, T2DecayResult
64
65
 
65
66
  from cirq.experiments.xeb_fitting import XEBPhasedFSimCharacterizationOptions
67
+
68
+ from cirq.experiments.two_qubit_xeb import (
69
+ InferredXEBResult,
70
+ TwoQubitXEBResult,
71
+ parallel_two_qubit_xeb,
72
+ run_rb_and_xeb,
73
+ )
@@ -14,15 +14,32 @@
14
14
 
15
15
  import dataclasses
16
16
  import itertools
17
-
18
- from typing import Any, cast, Iterator, List, Optional, Sequence, Tuple, TYPE_CHECKING
17
+ import functools
18
+
19
+ from typing import (
20
+ Any,
21
+ cast,
22
+ Iterator,
23
+ List,
24
+ Optional,
25
+ Sequence,
26
+ Tuple,
27
+ TYPE_CHECKING,
28
+ Mapping,
29
+ Dict,
30
+ )
19
31
  import numpy as np
32
+ from scipy.optimize import curve_fit
20
33
 
21
34
  from matplotlib import pyplot as plt
35
+ import cirq.vis.heatmap as cirq_heatmap
36
+ import cirq.vis.histogram as cirq_histogram
22
37
 
23
38
  # this is for older systems with matplotlib <3.2 otherwise 3d projections fail
24
39
  from mpl_toolkits import mplot3d
25
40
  from cirq import circuits, ops, protocols
41
+ from cirq.devices import grid_qubit
42
+
26
43
 
27
44
  if TYPE_CHECKING:
28
45
  import cirq
@@ -46,11 +63,11 @@ class Cliffords:
46
63
  s1_y
47
64
  """
48
65
 
49
- c1_in_xy: List[List[ops.Gate]]
50
- c1_in_xz: List[List[ops.Gate]]
51
- s1: List[List[ops.Gate]]
52
- s1_x: List[List[ops.Gate]]
53
- s1_y: List[List[ops.Gate]]
66
+ c1_in_xy: List[List[ops.SingleQubitCliffordGate]]
67
+ c1_in_xz: List[List[ops.SingleQubitCliffordGate]]
68
+ s1: List[List[ops.SingleQubitCliffordGate]]
69
+ s1_x: List[List[ops.SingleQubitCliffordGate]]
70
+ s1_y: List[List[ops.SingleQubitCliffordGate]]
54
71
 
55
72
 
56
73
  class RandomizedBenchMarkResult:
@@ -92,13 +109,165 @@ class RandomizedBenchMarkResult:
92
109
  fig, ax = plt.subplots(1, 1, figsize=(8, 8)) # pragma: no cover
93
110
  ax = cast(plt.Axes, ax) # pragma: no cover
94
111
  ax.set_ylim((0.0, 1.0)) # pragma: no cover
95
- ax.plot(self._num_cfds_seq, self._gnd_state_probs, 'ro-', **plot_kwargs)
112
+ ax.plot(self._num_cfds_seq, self._gnd_state_probs, 'ro', label='data', **plot_kwargs)
113
+ x = np.linspace(self._num_cfds_seq[0], self._num_cfds_seq[-1], 100)
114
+ opt_params, _ = self._fit_exponential()
115
+ ax.plot(x, opt_params[0] * opt_params[2] ** x + opt_params[1], '--k', label='fit')
116
+ ax.legend(loc='upper right')
96
117
  ax.set_xlabel(r"Number of Cliffords")
97
118
  ax.set_ylabel('Ground State Probability')
98
119
  if show_plot:
99
120
  fig.show()
100
121
  return ax
101
122
 
123
+ def pauli_error(self) -> float:
124
+ r"""Returns the Pauli error inferred from randomized benchmarking.
125
+
126
+ If sequence fidelity $F$ decays with number of gates $m$ as
127
+
128
+ $$F = A p^m + B,$$
129
+
130
+ where $0 < p < 1$, then the Pauli error $r_p$ is given by
131
+
132
+ $$r_p = (1 - 1/d^2) * (1 - p),$$
133
+
134
+ where $d = 2^N_Q$ is the Hilbert space dimension and $N_Q$ is the number of qubits.
135
+ """
136
+ opt_params, _ = self._fit_exponential()
137
+ p = opt_params[2]
138
+ return (1.0 - 1.0 / 4.0) * (1.0 - p)
139
+
140
+ def _fit_exponential(self) -> Tuple[np.ndarray, np.ndarray]:
141
+ exp_fit = lambda x, A, B, p: A * p**x + B
142
+ return curve_fit(
143
+ f=exp_fit,
144
+ xdata=self._num_cfds_seq,
145
+ ydata=self._gnd_state_probs,
146
+ p0=[0.5, 0.5, 1.0 - 1e-3],
147
+ bounds=([0, 0.25, 0], [0.5, 0.75, 1]),
148
+ )
149
+
150
+
151
+ @dataclasses.dataclass(frozen=True)
152
+ class ParallelRandomizedBenchmarkingResult:
153
+ """Results from a parallel randomized benchmarking experiment."""
154
+
155
+ results_dictionary: Mapping['cirq.Qid', 'RandomizedBenchMarkResult']
156
+
157
+ def plot_single_qubit(
158
+ self, qubit: 'cirq.Qid', ax: Optional[plt.Axes] = None, **plot_kwargs: Any
159
+ ) -> plt.Axes:
160
+ """Plot the raw data for the specified qubit.
161
+
162
+ Args:
163
+ qubit: Plot data for this qubit.
164
+ ax: the plt.Axes to plot on. If not given, a new figure is created,
165
+ plotted on, and shown.
166
+ **plot_kwargs: Arguments to be passed to 'plt.Axes.plot'.
167
+ Returns:
168
+ The plt.Axes containing the plot.
169
+ """
170
+
171
+ return self.results_dictionary[qubit].plot(ax, **plot_kwargs)
172
+
173
+ def pauli_error(self) -> Mapping['cirq.Qid', float]:
174
+ """Return a dictionary of Pauli errors.
175
+ Returns:
176
+ A dictionary containing the Pauli errors for all qubits.
177
+ """
178
+
179
+ return {
180
+ qubit: self.results_dictionary[qubit].pauli_error() for qubit in self.results_dictionary
181
+ }
182
+
183
+ def plot_heatmap(
184
+ self,
185
+ ax: Optional[plt.Axes] = None,
186
+ annotation_format: str = '0.1%',
187
+ title: str = 'Single-qubit Pauli error',
188
+ **plot_kwargs: Any,
189
+ ) -> plt.Axes:
190
+ """Plot a heatmap of the Pauli errors. If qubits are not cirq.GridQubits, throws an error.
191
+
192
+ Args:
193
+ ax: the plt.Axes to plot on. If not given, a new figure is created,
194
+ plotted on, and shown.
195
+ annotation_format: The format string for the numbers in the heatmap.
196
+ title: The title printed above the heatmap.
197
+ **plot_kwargs: Arguments to be passed to 'cirq.Heatmap.plot()'.
198
+ Returns:
199
+ The plt.Axes containing the plot.
200
+ """
201
+
202
+ pauli_errors = self.pauli_error()
203
+ pauli_errors_with_grid_qubit_keys = {}
204
+ for qubit in pauli_errors:
205
+ assert type(qubit) == grid_qubit.GridQubit, "qubits must be cirq.GridQubits"
206
+ pauli_errors_with_grid_qubit_keys[qubit] = pauli_errors[qubit] # just for typecheck
207
+
208
+ if ax is None:
209
+ _, ax = plt.subplots(dpi=200, facecolor='white')
210
+
211
+ ax, _ = cirq_heatmap.Heatmap(pauli_errors_with_grid_qubit_keys).plot(
212
+ ax, annotation_format=annotation_format, title=title, **plot_kwargs
213
+ )
214
+ return ax
215
+
216
+ def plot_integrated_histogram(
217
+ self,
218
+ ax: Optional[plt.Axes] = None,
219
+ cdf_on_x: bool = False,
220
+ axis_label: str = 'Pauli error',
221
+ semilog: bool = True,
222
+ median_line: bool = True,
223
+ median_label: Optional[str] = 'median',
224
+ mean_line: bool = False,
225
+ mean_label: Optional[str] = 'mean',
226
+ show_zero: bool = False,
227
+ title: Optional[str] = None,
228
+ **kwargs,
229
+ ) -> plt.Axes:
230
+ """Plot the Pauli errors using cirq.integrated_histogram().
231
+
232
+ Args:
233
+ ax: The axis to plot on. If None, we generate one.
234
+ cdf_on_x: If True, flip the axes compared the above example.
235
+ axis_label: Label for x axis (y-axis if cdf_on_x is True).
236
+ semilog: If True, force the x-axis to be logarithmic.
237
+ median_line: If True, draw a vertical line on the median value.
238
+ median_label: If drawing median line, optional label for it.
239
+ mean_line: If True, draw a vertical line on the mean value.
240
+ mean_label: If drawing mean line, optional label for it.
241
+ title: Title of the plot. If None, we assign "N={len(data)}".
242
+ show_zero: If True, moves the step plot up by one unit by prepending 0
243
+ to the data.
244
+ **kwargs: Kwargs to forward to `ax.step()`. Some examples are
245
+ color: Color of the line.
246
+ linestyle: Linestyle to use for the plot.
247
+ lw: linewidth for integrated histogram.
248
+ ms: marker size for a histogram trace.
249
+ label: An optional label which can be used in a legend.
250
+ Returns:
251
+ The axis that was plotted on.
252
+ """
253
+
254
+ ax = cirq_histogram.integrated_histogram(
255
+ data=self.pauli_error(),
256
+ ax=ax,
257
+ cdf_on_x=cdf_on_x,
258
+ axis_label=axis_label,
259
+ semilog=semilog,
260
+ median_line=median_line,
261
+ median_label=median_label,
262
+ mean_line=mean_line,
263
+ mean_label=mean_label,
264
+ show_zero=show_zero,
265
+ title=title,
266
+ **kwargs,
267
+ )
268
+ ax.set_ylabel('Percentile')
269
+ return ax
270
+
102
271
 
103
272
  class TomographyResult:
104
273
  """Results from a state tomography experiment."""
@@ -175,9 +344,9 @@ def single_qubit_randomized_benchmarking(
175
344
  qubit: 'cirq.Qid',
176
345
  use_xy_basis: bool = True,
177
346
  *,
178
- num_clifford_range: Sequence[int] = range(10, 100, 10),
179
- num_circuits: int = 20,
180
- repetitions: int = 1000,
347
+ num_clifford_range: Sequence[int] = tuple(np.logspace(np.log10(5), 3, 5, dtype=int)),
348
+ num_circuits: int = 10,
349
+ repetitions: int = 600,
181
350
  ) -> RandomizedBenchMarkResult:
182
351
  """Clifford-based randomized benchmarking (RB) of a single qubit.
183
352
 
@@ -213,21 +382,73 @@ def single_qubit_randomized_benchmarking(
213
382
  A RandomizedBenchMarkResult object that stores and plots the result.
214
383
  """
215
384
 
216
- cliffords = _single_qubit_cliffords()
217
- c1 = cliffords.c1_in_xy if use_xy_basis else cliffords.c1_in_xz
218
- cfd_mats = np.array([_gate_seq_to_mats(gates) for gates in c1])
385
+ result = parallel_single_qubit_randomized_benchmarking(
386
+ sampler,
387
+ (qubit,),
388
+ use_xy_basis,
389
+ num_clifford_range=num_clifford_range,
390
+ num_circuits=num_circuits,
391
+ repetitions=repetitions,
392
+ )
393
+ return result.results_dictionary[qubit]
219
394
 
220
- gnd_probs = []
221
- for num_cfds in num_clifford_range:
222
- excited_probs_l = []
223
- for _ in range(num_circuits):
224
- circuit = _random_single_q_clifford(qubit, num_cfds, c1, cfd_mats)
225
- circuit.append(ops.measure(qubit, key='z'))
226
- results = sampler.run(circuit, repetitions=repetitions)
227
- excited_probs_l.append(np.mean(results.measurements['z']))
228
- gnd_probs.append(1.0 - np.mean(excited_probs_l))
229
395
 
230
- return RandomizedBenchMarkResult(num_clifford_range, gnd_probs)
396
+ def parallel_single_qubit_randomized_benchmarking(
397
+ sampler: 'cirq.Sampler',
398
+ qubits: Sequence['cirq.Qid'],
399
+ use_xy_basis: bool = True,
400
+ *,
401
+ num_clifford_range: Sequence[int] = tuple(
402
+ np.logspace(np.log10(5), np.log10(1000), 5, dtype=int)
403
+ ),
404
+ num_circuits: int = 10,
405
+ repetitions: int = 1000,
406
+ ) -> 'ParallelRandomizedBenchmarkingResult':
407
+ """Clifford-based randomized benchmarking (RB) single qubits in parallel.
408
+
409
+ This is the same as `single_qubit_randomized_benchmarking` except on all
410
+ of the specified qubits in parallel, i.e. with the individual randomized
411
+ benchmarking circuits zipped together.
412
+
413
+ Args:
414
+ sampler: The quantum engine or simulator to run the circuits.
415
+ use_xy_basis: Determines if the Clifford gates are built with x and y
416
+ rotations (True) or x and z rotations (False).
417
+ qubits: The qubits to benchmark.
418
+ num_clifford_range: The different numbers of Cliffords in the RB study.
419
+ num_circuits: The number of random circuits generated for each
420
+ number of Cliffords.
421
+ repetitions: The number of repetitions of each circuit.
422
+
423
+ Returns:
424
+ A dictionary from qubits to RandomizedBenchMarkResult objects.
425
+ """
426
+
427
+ clifford_group = _single_qubit_cliffords()
428
+ c1 = clifford_group.c1_in_xy if use_xy_basis else clifford_group.c1_in_xz
429
+
430
+ # create circuits
431
+ circuits_all: List['cirq.AbstractCircuit'] = []
432
+ for num_cliffords in num_clifford_range:
433
+ for _ in range(num_circuits):
434
+ circuits_all.append(_create_parallel_rb_circuit(qubits, num_cliffords, c1))
435
+
436
+ # run circuits
437
+ results = sampler.run_batch(circuits_all, repetitions=repetitions)
438
+ gnd_probs: dict = {q: [] for q in qubits}
439
+ idx = 0
440
+ for num_cliffords in num_clifford_range:
441
+ excited_probs: Dict['cirq.Qid', List[float]] = {q: [] for q in qubits}
442
+ for _ in range(num_circuits):
443
+ result = results[idx][0]
444
+ for qubit in qubits:
445
+ excited_probs[qubit].append(np.mean(result.measurements[str(qubit)]))
446
+ idx += 1
447
+ for qubit in qubits:
448
+ gnd_probs[qubit].append(1.0 - np.mean(excited_probs[qubit]))
449
+ return ParallelRandomizedBenchmarkingResult(
450
+ {q: RandomizedBenchMarkResult(num_clifford_range, gnd_probs[q]) for q in qubits}
451
+ )
231
452
 
232
453
 
233
454
  def two_qubit_randomized_benchmarking(
@@ -464,6 +685,21 @@ def two_qubit_state_tomography(
464
685
  return TomographyResult(rho)
465
686
 
466
687
 
688
+ def _create_parallel_rb_circuit(
689
+ qubits: Sequence['cirq.Qid'], num_cliffords: int, c1: list
690
+ ) -> 'cirq.Circuit':
691
+ sequences_to_zip = [_random_single_q_clifford(qubit, num_cliffords, c1) for qubit in qubits]
692
+ # Ensure each sequence has the same number of moments.
693
+ num_moments = max(len(sequence) for sequence in sequences_to_zip)
694
+ for q, sequence in zip(qubits, sequences_to_zip):
695
+ if (n := len(sequence)) < num_moments:
696
+ sequence.extend(
697
+ [ops.SingleQubitCliffordGate.I.to_phased_xz_gate()(q)] * (num_moments - n)
698
+ )
699
+ moments = zip(*sequences_to_zip)
700
+ return circuits.Circuit.from_moments(*moments, ops.measure_each(*qubits))
701
+
702
+
467
703
  def _indices_after_basis_rot(i: int, j: int) -> Tuple[int, Sequence[int], Sequence[int]]:
468
704
  mat_idx = 3 * (3 * i + j)
469
705
  q_0_i = 3 - i
@@ -505,18 +741,15 @@ def _two_qubit_clifford_matrices(
505
741
 
506
742
 
507
743
  def _random_single_q_clifford(
508
- qubit: 'cirq.Qid',
509
- num_cfds: int,
510
- cfds: Sequence[Sequence['cirq.Gate']],
511
- cfd_matrices: np.ndarray,
512
- ) -> 'cirq.Circuit':
744
+ qubit: 'cirq.Qid', num_cfds: int, cfds: Sequence[Sequence['cirq.ops.SingleQubitCliffordGate']]
745
+ ) -> List['cirq.Operation']:
513
746
  clifford_group_size = 24
747
+ operations = [[gate.to_phased_xz_gate()(qubit) for gate in gates] for gates in cfds]
514
748
  gate_ids = list(np.random.choice(clifford_group_size, num_cfds))
515
- gate_sequence = [gate for gate_id in gate_ids for gate in cfds[gate_id]]
516
- idx = _find_inv_matrix(_gate_seq_to_mats(gate_sequence), cfd_matrices)
517
- gate_sequence.extend(cfds[idx])
518
- circuit = circuits.Circuit(gate(qubit) for gate in gate_sequence)
519
- return circuit
749
+ adjoint = _reduce_gate_seq([gate for gate_id in gate_ids for gate in cfds[gate_id]]) ** -1
750
+ return [op for gate_id in gate_ids for op in operations[gate_id]] + [
751
+ adjoint.to_phased_xz_gate()(qubit)
752
+ ]
520
753
 
521
754
 
522
755
  def _random_two_q_clifford(
@@ -574,11 +807,13 @@ def _matrix_bar_plot(
574
807
  ax.set_title(title)
575
808
 
576
809
 
577
- def _gate_seq_to_mats(gate_seq: Sequence['cirq.Gate']) -> np.ndarray:
578
- mat_rep = protocols.unitary(gate_seq[0])
810
+ def _reduce_gate_seq(
811
+ gate_seq: Sequence[ops.SingleQubitCliffordGate],
812
+ ) -> ops.SingleQubitCliffordGate:
813
+ cur = gate_seq[0]
579
814
  for gate in gate_seq[1:]:
580
- mat_rep = np.dot(protocols.unitary(gate), mat_rep)
581
- return mat_rep
815
+ cur = cur.merged_with(gate)
816
+ return cur
582
817
 
583
818
 
584
819
  def _two_qubit_clifford(
@@ -686,11 +921,16 @@ def _single_qubit_gates(
686
921
  yield gate(qubit)
687
922
 
688
923
 
924
+ @functools.cache
689
925
  def _single_qubit_cliffords() -> Cliffords:
690
- X, Y, Z = ops.X, ops.Y, ops.Z
926
+ X, Y, Z = (
927
+ ops.SingleQubitCliffordGate.X,
928
+ ops.SingleQubitCliffordGate.Y,
929
+ ops.SingleQubitCliffordGate.Z,
930
+ )
691
931
 
692
- c1_in_xy: List[List['cirq.Gate']] = []
693
- c1_in_xz: List[List['cirq.Gate']] = []
932
+ c1_in_xy: List[List[ops.SingleQubitCliffordGate]] = []
933
+ c1_in_xz: List[List[ops.SingleQubitCliffordGate]] = []
694
934
 
695
935
  for phi_0, phi_1 in itertools.product([1.0, 0.5, -0.5], [0.0, 0.5, -0.5]):
696
936
  c1_in_xy.append([X**phi_0, Y**phi_1])
@@ -713,8 +953,12 @@ def _single_qubit_cliffords() -> Cliffords:
713
953
  for z0, x, z1 in phi_xz:
714
954
  c1_in_xz.append([Z**z0, X**x, Z**z1])
715
955
 
716
- s1: List[List['cirq.Gate']] = [[X**0.0], [Y**0.5, X**0.5], [X**-0.5, Y**-0.5]]
717
- s1_x: List[List['cirq.Gate']] = [[X**0.5], [X**0.5, Y**0.5, X**0.5], [Y**-0.5]]
718
- s1_y: List[List['cirq.Gate']] = [[Y**0.5], [X**-0.5, Y**-0.5, X**0.5], [Y, X**0.5]]
956
+ s1: List[List[ops.SingleQubitCliffordGate]] = [[X**0.0], [Y**0.5, X**0.5], [X**-0.5, Y**-0.5]]
957
+ s1_x: List[List[ops.SingleQubitCliffordGate]] = [[X**0.5], [X**0.5, Y**0.5, X**0.5], [Y**-0.5]]
958
+ s1_y: List[List[ops.SingleQubitCliffordGate]] = [
959
+ [Y**0.5],
960
+ [X**-0.5, Y**-0.5, X**0.5],
961
+ [Y, X**0.5],
962
+ ]
719
963
 
720
964
  return Cliffords(c1_in_xy, c1_in_xz, s1, s1_x, s1_y)
@@ -26,6 +26,7 @@ from cirq.experiments import (
26
26
  two_qubit_randomized_benchmarking,
27
27
  single_qubit_state_tomography,
28
28
  two_qubit_state_tomography,
29
+ parallel_single_qubit_randomized_benchmarking,
29
30
  )
30
31
 
31
32
 
@@ -74,9 +75,32 @@ def test_single_qubit_cliffords():
74
75
 
75
76
  # Check that XZ decomposition has at most one X gate per clifford.
76
77
  for gates in cliffords.c1_in_xz:
77
- num_x = len([gate for gate in gates if isinstance(gate, cirq.XPowGate)])
78
- num_z = len([gate for gate in gates if isinstance(gate, cirq.ZPowGate)])
79
- assert num_x + num_z == len(gates)
78
+ num_i = len([gate for gate in gates if gate == cirq.ops.SingleQubitCliffordGate.I])
79
+ num_x = len(
80
+ [
81
+ gate
82
+ for gate in gates
83
+ if gate
84
+ in (
85
+ cirq.ops.SingleQubitCliffordGate.X,
86
+ cirq.ops.SingleQubitCliffordGate.X_sqrt,
87
+ cirq.ops.SingleQubitCliffordGate.X_nsqrt,
88
+ )
89
+ ]
90
+ )
91
+ num_z = len(
92
+ [
93
+ gate
94
+ for gate in gates
95
+ if gate
96
+ in (
97
+ cirq.ops.SingleQubitCliffordGate.Z,
98
+ cirq.ops.SingleQubitCliffordGate.Z_sqrt,
99
+ cirq.ops.SingleQubitCliffordGate.Z_nsqrt,
100
+ )
101
+ ]
102
+ )
103
+ assert num_x + num_z + num_i == len(gates)
80
104
  assert num_x <= 1
81
105
 
82
106
 
@@ -85,12 +109,30 @@ def test_single_qubit_randomized_benchmarking():
85
109
  # sequences is always unity.
86
110
  simulator = sim.Simulator()
87
111
  qubit = GridQubit(0, 0)
88
- num_cfds = range(5, 20, 5)
89
- results = single_qubit_randomized_benchmarking(
90
- simulator, qubit, num_clifford_range=num_cfds, repetitions=100
91
- )
112
+ num_cfds = tuple(np.logspace(np.log10(5), 3, 5, dtype=int))
113
+ results = single_qubit_randomized_benchmarking(simulator, qubit, num_clifford_range=num_cfds)
92
114
  g_pops = np.asarray(results.data)[:, 1]
93
115
  assert np.isclose(np.mean(g_pops), 1.0)
116
+ assert np.isclose(results.pauli_error(), 0.0, atol=1e-7) # warning is expected
117
+
118
+
119
+ def test_parallel_single_qubit_randomized_benchmarking():
120
+ # Check that the ground state population at the end of the Clifford
121
+ # sequences is always unity.
122
+ simulator = sim.Simulator()
123
+ qubits = (GridQubit(0, 0), GridQubit(0, 1))
124
+ num_cfds = range(5, 20, 5)
125
+ results = parallel_single_qubit_randomized_benchmarking(
126
+ simulator, num_clifford_range=num_cfds, repetitions=100, qubits=qubits
127
+ )
128
+ for qubit in qubits:
129
+ g_pops = np.asarray(results.results_dictionary[qubit].data)[:, 1]
130
+ assert np.isclose(np.mean(g_pops), 1.0)
131
+ _ = results.plot_single_qubit(qubit)
132
+ pauli_errors = results.pauli_error()
133
+ assert len(pauli_errors) == len(qubits)
134
+ _ = results.plot_heatmap()
135
+ _ = results.plot_integrated_histogram()
94
136
 
95
137
 
96
138
  def test_two_qubit_randomized_benchmarking():
@@ -185,3 +227,15 @@ def test_tomography_plot_raises_for_incorrect_number_of_axes():
185
227
  with pytest.raises(ValueError):
186
228
  _, axes = plt.subplots(1, 3)
187
229
  result.plot(axes)
230
+
231
+
232
+ def test_single_qubit_cliffords_gateset():
233
+ qubits = [GridQubit(0, i) for i in range(4)]
234
+ clifford_group = cirq.experiments.qubit_characterizations._single_qubit_cliffords()
235
+ c = cirq.experiments.qubit_characterizations._create_parallel_rb_circuit(
236
+ qubits, 5, clifford_group.c1_in_xy
237
+ )
238
+ device = cirq.testing.ValidatingTestDevice(
239
+ qubits=qubits, allowed_gates=(cirq.ops.PhasedXZGate, cirq.MeasurementGate)
240
+ )
241
+ device.validate_circuit(c)
@@ -142,7 +142,7 @@ document(
142
142
 
143
143
  This pattern of gates was used in the paper
144
144
  https://www.nature.com/articles/s41586-019-1666-5
145
- to demonstrate quantum supremacy.
145
+ to demonstrate beyond-classical computation.
146
146
  """,
147
147
  )
148
148