cirq-core 1.3.0.dev20231201164435__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.dev20231201164435.dist-info → cirq_core-1.4.0.dist-info}/METADATA +10 -19
  154. {cirq_core-1.3.0.dev20231201164435.dist-info → cirq_core-1.4.0.dist-info}/RECORD +157 -130
  155. {cirq_core-1.3.0.dev20231201164435.dist-info → cirq_core-1.4.0.dist-info}/WHEEL +1 -1
  156. {cirq_core-1.3.0.dev20231201164435.dist-info → cirq_core-1.4.0.dist-info}/LICENSE +0 -0
  157. {cirq_core-1.3.0.dev20231201164435.dist-info → cirq_core-1.4.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,483 @@
1
+ # Copyright 2024 The Cirq Developers
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """Provides functions for running and analyzing two-qubit XEB experiments."""
16
+ from typing import Sequence, TYPE_CHECKING, Optional, Tuple, Dict, cast, Mapping
17
+
18
+ from dataclasses import dataclass
19
+ from types import MappingProxyType
20
+ import itertools
21
+ import functools
22
+
23
+ from matplotlib import pyplot as plt
24
+ import networkx as nx
25
+ import numpy as np
26
+ import pandas as pd
27
+
28
+ from cirq import ops, value, vis
29
+ from cirq.experiments.xeb_sampling import sample_2q_xeb_circuits
30
+ from cirq.experiments.xeb_fitting import benchmark_2q_xeb_fidelities
31
+ from cirq.experiments.xeb_fitting import fit_exponential_decays, exponential_decay
32
+ from cirq.experiments import random_quantum_circuit_generation as rqcg
33
+ from cirq.experiments.qubit_characterizations import (
34
+ ParallelRandomizedBenchmarkingResult,
35
+ parallel_single_qubit_randomized_benchmarking,
36
+ )
37
+ from cirq.qis import noise_utils
38
+ from cirq._compat import cached_method
39
+
40
+ if TYPE_CHECKING:
41
+ import cirq
42
+
43
+
44
+ def _grid_qubits_for_sampler(sampler: 'cirq.Sampler') -> Optional[Sequence['cirq.GridQubit']]:
45
+ if hasattr(sampler, 'processor'):
46
+ device = sampler.processor.get_device()
47
+ return sorted(device.metadata.qubit_set)
48
+ return None
49
+
50
+
51
+ def _manhattan_distance(qubit1: 'cirq.GridQubit', qubit2: 'cirq.GridQubit') -> int:
52
+ return abs(qubit1.row - qubit2.row) + abs(qubit1.col - qubit2.col)
53
+
54
+
55
+ @dataclass(frozen=True)
56
+ class TwoQubitXEBResult:
57
+ """Results from an XEB experiment."""
58
+
59
+ fidelities: pd.DataFrame
60
+
61
+ @functools.cached_property
62
+ def _qubit_pair_map(self) -> Dict[Tuple['cirq.GridQubit', 'cirq.GridQubit'], int]:
63
+ return {
64
+ (min(q0, q1), max(q0, q1)): i
65
+ for i, (_, _, (q0, q1)) in enumerate(self.fidelities.index)
66
+ }
67
+
68
+ @functools.cached_property
69
+ def all_qubit_pairs(self) -> Tuple[Tuple['cirq.GridQubit', 'cirq.GridQubit'], ...]:
70
+ return tuple(sorted(self._qubit_pair_map.keys()))
71
+
72
+ def plot_heatmap(self, ax: Optional[plt.Axes] = None, **plot_kwargs) -> plt.Axes:
73
+ """plot the heatmap of XEB errors.
74
+
75
+ Args:
76
+ ax: the plt.Axes to plot on. If not given, a new figure is created,
77
+ plotted on, and shown.
78
+ **plot_kwargs: Arguments to be passed to 'plt.Axes.plot'.
79
+
80
+ Returns:
81
+ The plt.Axes that was plotted on.
82
+ """
83
+ show_plot = not ax
84
+ if not isinstance(ax, plt.Axes):
85
+ fig, ax = plt.subplots(1, 1, figsize=(8, 8))
86
+ heatmap_data: Dict[Tuple['cirq.GridQubit', ...], float] = {
87
+ pair: self.xeb_error(*pair) for pair in self.all_qubit_pairs
88
+ }
89
+
90
+ ax.set_title('device xeb error heatmap')
91
+
92
+ vis.TwoQubitInteractionHeatmap(heatmap_data).plot(ax=ax, **plot_kwargs)
93
+ if show_plot:
94
+ fig.show()
95
+ return ax
96
+
97
+ def plot_fitted_exponential(
98
+ self,
99
+ q0: 'cirq.GridQubit',
100
+ q1: 'cirq.GridQubit',
101
+ ax: Optional[plt.Axes] = None,
102
+ **plot_kwargs,
103
+ ) -> plt.Axes:
104
+ """plot the fitted model to for xeb error of a qubit pair.
105
+
106
+ Args:
107
+ q0: first qubit.
108
+ q1: second qubit.
109
+ ax: the plt.Axes to plot on. If not given, a new figure is created,
110
+ plotted on, and shown.
111
+ **plot_kwargs: Arguments to be passed to 'plt.Axes.plot'.
112
+
113
+ Returns:
114
+ The plt.Axes that was plotted on.
115
+ """
116
+ show_plot = not ax
117
+ if not isinstance(ax, plt.Axes):
118
+ fig, ax = plt.subplots(1, 1, figsize=(8, 8))
119
+
120
+ record = self._record(q0, q1)
121
+
122
+ ax.axhline(1, color='grey', ls='--')
123
+ ax.plot(record['cycle_depths'], record['fidelities'], 'o')
124
+ depths = np.linspace(0, np.max(record['cycle_depths']))
125
+ ax.plot(
126
+ depths,
127
+ exponential_decay(depths, a=record['a'], layer_fid=record['layer_fid']),
128
+ label='estimated exponential decay',
129
+ **plot_kwargs,
130
+ )
131
+ ax.set_title(f'{q0}-{q1}')
132
+ ax.set_ylabel('Circuit fidelity')
133
+ ax.set_xlabel('Cycle Depth $d$')
134
+ ax.legend(loc='best')
135
+ if show_plot:
136
+ fig.show()
137
+ return ax
138
+
139
+ def _record(self, q0, q1) -> pd.Series:
140
+ if q0 > q1:
141
+ q0, q1 = q1, q0
142
+ return self.fidelities.iloc[self._qubit_pair_map[(q0, q1)]]
143
+
144
+ def xeb_fidelity(self, q0: 'cirq.GridQubit', q1: 'cirq.GridQubit') -> float:
145
+ """Return the XEB fidelity of a qubit pair."""
146
+ return self._record(q0, q1).layer_fid
147
+
148
+ def xeb_error(self, q0: 'cirq.GridQubit', q1: 'cirq.GridQubit') -> float:
149
+ """Return the XEB error of a qubit pair."""
150
+ return 1 - self.xeb_fidelity(q0, q1)
151
+
152
+ def all_errors(self) -> Dict[Tuple['cirq.GridQubit', 'cirq.GridQubit'], float]:
153
+ """Return the XEB error of all qubit pairs."""
154
+ return {(q0, q1): self.xeb_error(q0, q1) for q0, q1 in self.all_qubit_pairs}
155
+
156
+ def plot_histogram(self, ax: Optional[plt.Axes] = None, **plot_kwargs) -> plt.Axes:
157
+ """plot a histogram of all xeb errors.
158
+
159
+ Args:
160
+ ax: the plt.Axes to plot on. If not given, a new figure is created,
161
+ plotted on, and shown.
162
+ **plot_kwargs: Arguments to be passed to 'plt.Axes.plot'.
163
+
164
+ Returns:
165
+ The plt.Axes that was plotted on.
166
+ """
167
+ fig = None
168
+ if ax is None:
169
+ fig, ax = plt.subplots(1, 1, figsize=(8, 8))
170
+ vis.integrated_histogram(data=self.all_errors(), ax=ax, **plot_kwargs)
171
+ if fig is not None:
172
+ fig.show(**plot_kwargs)
173
+ return ax
174
+
175
+ @cached_method
176
+ def pauli_error(self) -> Dict[Tuple['cirq.GridQubit', 'cirq.GridQubit'], float]:
177
+ """Return the Pauli error of all qubit pairs."""
178
+ return {
179
+ pair: noise_utils.decay_constant_to_pauli_error(
180
+ noise_utils.xeb_fidelity_to_decay_constant(self.xeb_fidelity(*pair), num_qubits=2),
181
+ num_qubits=2,
182
+ )
183
+ for pair in self.all_qubit_pairs
184
+ }
185
+
186
+
187
+ @dataclass(frozen=True)
188
+ class InferredXEBResult:
189
+ """Uses the results from XEB and RB to compute inferred two-qubit Pauli errors.
190
+
191
+ The result of running just XEB combines both two-qubit and single-qubit error rates,
192
+ this class computes inferred errors which are the result of removing the single qubit errors
193
+ from the two-qubit errors.
194
+ """
195
+
196
+ rb_result: ParallelRandomizedBenchmarkingResult
197
+ xeb_result: TwoQubitXEBResult
198
+
199
+ @property
200
+ def all_qubit_pairs(self) -> Sequence[Tuple['cirq.GridQubit', 'cirq.GridQubit']]:
201
+ return self.xeb_result.all_qubit_pairs
202
+
203
+ @cached_method
204
+ def single_qubit_pauli_error(self) -> Mapping['cirq.Qid', float]:
205
+ """Return the single-qubit Pauli error for all qubits (RB results)."""
206
+ return self.rb_result.pauli_error()
207
+
208
+ @cached_method
209
+ def two_qubit_pauli_error(self) -> Mapping[Tuple['cirq.GridQubit', 'cirq.GridQubit'], float]:
210
+ """Return the two-qubit Pauli error for all pairs."""
211
+ return MappingProxyType(self.xeb_result.pauli_error())
212
+
213
+ @cached_method
214
+ def inferred_pauli_error(self) -> Mapping[Tuple['cirq.GridQubit', 'cirq.GridQubit'], float]:
215
+ """Return the inferred Pauli error for all pairs."""
216
+ single_q_paulis = self.rb_result.pauli_error()
217
+ xeb = self.xeb_result.pauli_error()
218
+
219
+ def _pauli_error(q0: 'cirq.GridQubit', q1: 'cirq.GridQubit') -> float:
220
+ q0, q1 = sorted([q0, q1])
221
+ return xeb[(q0, q1)] - single_q_paulis[q0] - single_q_paulis[q1]
222
+
223
+ return MappingProxyType({pair: _pauli_error(*pair) for pair in self.all_qubit_pairs})
224
+
225
+ @cached_method
226
+ def inferred_decay_constant(self) -> Mapping[Tuple['cirq.GridQubit', 'cirq.GridQubit'], float]:
227
+ """Return the inferred decay constant for all pairs."""
228
+ return MappingProxyType(
229
+ {
230
+ pair: noise_utils.pauli_error_to_decay_constant(pauli, 2)
231
+ for pair, pauli in self.inferred_pauli_error().items()
232
+ }
233
+ )
234
+
235
+ @cached_method
236
+ def inferred_xeb_error(self) -> Mapping[Tuple['cirq.GridQubit', 'cirq.GridQubit'], float]:
237
+ """Return the inferred XEB error for all pairs."""
238
+ return MappingProxyType(
239
+ {
240
+ pair: 1 - noise_utils.decay_constant_to_xeb_fidelity(decay, 2)
241
+ for pair, decay in self.inferred_decay_constant().items()
242
+ }
243
+ )
244
+
245
+ def _target_errors(
246
+ self, target_error: str
247
+ ) -> Mapping[Tuple['cirq.GridQubit', 'cirq.GridQubit'], float]:
248
+ """Returns requested error.
249
+
250
+ The requested error must be one of 'pauli', 'decay_constant', or 'xeb'.
251
+
252
+ Args:
253
+ target_error: The error to draw.
254
+
255
+ Returns:
256
+ A mapping of qubit pairs to the requested error.
257
+
258
+ Raises:
259
+ ValueError: If the requested error is not one of 'pauli', 'decay_constant', or 'xeb'.
260
+ """
261
+ error_funcs = {
262
+ 'pauli': self.inferred_pauli_error,
263
+ 'decay_constant': self.inferred_decay_constant,
264
+ 'xeb': self.inferred_xeb_error,
265
+ }
266
+ return error_funcs[target_error]()
267
+
268
+ def plot_heatmap(
269
+ self, target_error: str = 'pauli', ax: Optional[plt.Axes] = None, **plot_kwargs
270
+ ) -> plt.Axes:
271
+ """plot the heatmap of the target errors.
272
+
273
+ Args:
274
+ target_error: The error to draw. Must be one of 'xeb', 'pauli', or 'decay_constant'
275
+ ax: the plt.Axes to plot on. If not given, a new figure is created,
276
+ plotted on, and shown.
277
+ **plot_kwargs: Arguments to be passed to 'plt.Axes.plot'.
278
+ """
279
+ show_plot = not ax
280
+ if not isinstance(ax, plt.Axes):
281
+ fig, ax = plt.subplots(1, 1, figsize=(8, 8))
282
+ heatmap_data = cast(
283
+ Mapping[Tuple['cirq.GridQubit', ...], float], self._target_errors(target_error)
284
+ )
285
+
286
+ name = f'{target_error} error' if target_error != 'decay_constant' else 'decay constant'
287
+ ax.set_title(f'device {name} heatmap')
288
+
289
+ vis.TwoQubitInteractionHeatmap(heatmap_data).plot(ax=ax, **plot_kwargs)
290
+ if show_plot:
291
+ fig.show()
292
+ return ax
293
+
294
+ def plot_histogram(
295
+ self,
296
+ target_error: str = 'pauli',
297
+ ax: Optional[plt.Axes] = None,
298
+ kind: str = 'two_qubit',
299
+ **plot_kwargs,
300
+ ) -> plt.Axes:
301
+ """plot a histogram of target error.
302
+
303
+ Args:
304
+ target_error: The error to draw. Must be one of 'xeb', 'pauli', or 'decay_constant'
305
+ ax: the plt.Axes to plot on. If not given, a new figure is created,
306
+ plotted on, and shown.
307
+ kind: Whether to plot the single-qubit RB errors ('single_qubit') or the
308
+ two-qubit inferred errors ('two_qubit') or both ('both').
309
+ **plot_kwargs: Arguments to be passed to 'plt.Axes.plot'.
310
+
311
+ Returns:
312
+ The plt.Axes that was plotted on.
313
+
314
+ Raises:
315
+ ValueError: If
316
+ - `kind` is not one of 'single_qubit', 'two_qubit', or 'both'.
317
+ - `target_error` is not one of 'pauli', 'xeb', or 'decay_constant'
318
+ - single qubit error is requested and `target_error` is not 'pauli'.
319
+ """
320
+ if kind not in ('single_qubit', 'two_qubit', 'both'):
321
+ raise ValueError(
322
+ f"kind must be one of 'single_qubit', 'two_qubit', or 'both', not {kind}"
323
+ )
324
+ if kind != 'two_qubit' and target_error != 'pauli':
325
+ raise ValueError(f'{target_error} is not supported for single qubits')
326
+ fig = None
327
+ if ax is None:
328
+ fig, ax = plt.subplots(1, 1, figsize=(8, 8))
329
+
330
+ alpha = 0.5 if kind == 'both' else 1.0
331
+ if kind == 'single_qubit' or kind == 'both':
332
+ self.rb_result.plot_integrated_histogram(
333
+ ax=ax, alpha=alpha, label='single qubit', color='green', **plot_kwargs
334
+ )
335
+ if kind == 'two_qubit' or kind == 'both':
336
+ vis.integrated_histogram(
337
+ data=self._target_errors(target_error),
338
+ ax=ax,
339
+ alpha=alpha,
340
+ label='two qubit',
341
+ color='blue',
342
+ **plot_kwargs,
343
+ )
344
+
345
+ if fig is not None:
346
+ fig.show(**plot_kwargs)
347
+ return ax
348
+
349
+
350
+ def parallel_two_qubit_xeb(
351
+ sampler: 'cirq.Sampler',
352
+ qubits: Optional[Sequence['cirq.GridQubit']] = None,
353
+ entangling_gate: 'cirq.Gate' = ops.CZ,
354
+ n_repetitions: int = 10**4,
355
+ n_combinations: int = 10,
356
+ n_circuits: int = 20,
357
+ cycle_depths: Sequence[int] = tuple(np.arange(3, 100, 20)),
358
+ random_state: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None,
359
+ ax: Optional[plt.Axes] = None,
360
+ **plot_kwargs,
361
+ ) -> TwoQubitXEBResult:
362
+ """A convenience method that runs the full XEB workflow.
363
+
364
+ Args:
365
+ sampler: The quantum engine or simulator to run the circuits.
366
+ qubits: Qubits under test. If none, uses all qubits on the sampler's device.
367
+ entangling_gate: The entangling gate to use.
368
+ n_repetitions: The number of repetitions to use.
369
+ n_combinations: The number of combinations to generate.
370
+ n_circuits: The number of circuits to generate.
371
+ cycle_depths: The cycle depths to use.
372
+ random_state: The random state to use.
373
+ ax: the plt.Axes to plot the device layout on. If not given,
374
+ no plot is created.
375
+ **plot_kwargs: Arguments to be passed to 'plt.Axes.plot'.
376
+
377
+ Returns:
378
+ A TwoQubitXEBResult object representing the results of the experiment.
379
+
380
+ Raises:
381
+ ValueError: If qubits are not specified and the sampler has no device.
382
+ """
383
+ rs = value.parse_random_state(random_state)
384
+
385
+ if qubits is None:
386
+ qubits = _grid_qubits_for_sampler(sampler)
387
+ if qubits is None:
388
+ raise ValueError("Couldn't determine qubits from sampler. Please specify them.")
389
+
390
+ graph = nx.Graph(
391
+ pair for pair in itertools.combinations(qubits, 2) if _manhattan_distance(*pair) == 1
392
+ )
393
+
394
+ if ax is not None:
395
+ nx.draw_networkx(graph, pos={q: (q.row, q.col) for q in qubits}, ax=ax)
396
+ ax.set_title('device layout')
397
+ ax.plot(**plot_kwargs)
398
+
399
+ circuit_library = rqcg.generate_library_of_2q_circuits(
400
+ n_library_circuits=n_circuits, two_qubit_gate=entangling_gate, random_state=rs
401
+ )
402
+
403
+ combs_by_layer = rqcg.get_random_combinations_for_device(
404
+ n_library_circuits=len(circuit_library),
405
+ n_combinations=n_combinations,
406
+ device_graph=graph,
407
+ random_state=rs,
408
+ )
409
+
410
+ sampled_df = sample_2q_xeb_circuits(
411
+ sampler=sampler,
412
+ circuits=circuit_library,
413
+ cycle_depths=cycle_depths,
414
+ combinations_by_layer=combs_by_layer,
415
+ shuffle=rs,
416
+ repetitions=n_repetitions,
417
+ )
418
+
419
+ fids = benchmark_2q_xeb_fidelities(
420
+ sampled_df=sampled_df, circuits=circuit_library, cycle_depths=cycle_depths
421
+ )
422
+
423
+ return TwoQubitXEBResult(fit_exponential_decays(fids))
424
+
425
+
426
+ def run_rb_and_xeb(
427
+ sampler: 'cirq.Sampler',
428
+ qubits: Optional[Sequence['cirq.GridQubit']] = None,
429
+ repetitions: int = 10**3,
430
+ num_circuits: int = 20,
431
+ num_clifford_range: Sequence[int] = tuple(
432
+ np.logspace(np.log10(5), np.log10(1000), 5, dtype=int)
433
+ ),
434
+ entangling_gate: 'cirq.Gate' = ops.CZ,
435
+ depths_xeb: Sequence[int] = tuple(np.arange(3, 100, 20)),
436
+ xeb_combinations: int = 10,
437
+ random_state: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None,
438
+ ) -> InferredXEBResult:
439
+ """A convenience method that runs both RB and XEB workflows.
440
+
441
+ Args:
442
+ sampler: The quantum engine or simulator to run the circuits.
443
+ qubits: Qubits under test. If none, uses all qubits on the sampler's device.
444
+ repetitions: The number of repetitions to use for RB and XEB.
445
+ num_circuits: The number of circuits to generate for RB and XEB.
446
+ num_clifford_range: The different numbers of Cliffords in the RB study.
447
+ entangling_gate: The entangling gate to use.
448
+ depths_xeb: The cycle depths to use for XEB.
449
+ xeb_combinations: The number of combinations to generate for XEB.
450
+ random_state: The random state to use.
451
+
452
+ Returns:
453
+ An InferredXEBResult object representing the results of the experiment.
454
+
455
+ Raises:
456
+ ValueError: If qubits are not specified and the sampler has no device.
457
+ """
458
+
459
+ if qubits is None:
460
+ qubits = _grid_qubits_for_sampler(sampler)
461
+ if qubits is None:
462
+ raise ValueError("Couldn't determine qubits from sampler. Please specify them.")
463
+
464
+ rb = parallel_single_qubit_randomized_benchmarking(
465
+ sampler=sampler,
466
+ qubits=qubits,
467
+ repetitions=repetitions,
468
+ num_circuits=num_circuits,
469
+ num_clifford_range=num_clifford_range,
470
+ )
471
+
472
+ xeb = parallel_two_qubit_xeb(
473
+ sampler=sampler,
474
+ qubits=qubits,
475
+ entangling_gate=entangling_gate,
476
+ n_repetitions=repetitions,
477
+ n_circuits=num_circuits,
478
+ cycle_depths=depths_xeb,
479
+ n_combinations=xeb_combinations,
480
+ random_state=random_state,
481
+ )
482
+
483
+ return InferredXEBResult(rb, xeb)