11
11
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
# See the License for the specific language governing permissions and
13
13
# limitations under the License.
14
+
15
+ """Provides functions for running and analyzing two-qubit XEB experiments."""
14
16
from typing import Sequence , TYPE_CHECKING , Optional , Tuple , Dict , cast , Mapping
15
17
16
18
from dataclasses import dataclass
28
30
from cirq .experiments .xeb_fitting import benchmark_2q_xeb_fidelities
29
31
from cirq .experiments .xeb_fitting import fit_exponential_decays , exponential_decay
30
32
from cirq .experiments import random_quantum_circuit_generation as rqcg
31
- from cirq .experiments .qubit_characterizations import ParallelRandomizedBenchmarkingResult
33
+ from cirq .experiments .qubit_characterizations import (
34
+ ParallelRandomizedBenchmarkingResult ,
35
+ parallel_single_qubit_randomized_benchmarking ,
36
+ )
32
37
from cirq .qis import noise_utils
33
38
from cirq ._compat import cached_method
34
39
@@ -71,6 +76,9 @@ def plot_heatmap(self, ax: Optional[plt.Axes] = None, **plot_kwargs) -> plt.Axes
71
76
ax: the plt.Axes to plot on. If not given, a new figure is created,
72
77
plotted on, and shown.
73
78
**plot_kwargs: Arguments to be passed to 'plt.Axes.plot'.
79
+
80
+ Returns:
81
+ The plt.Axes that was plotted on.
74
82
"""
75
83
show_plot = not ax
76
84
if not isinstance (ax , plt .Axes ):
@@ -101,6 +109,9 @@ def plot_fitted_exponential(
101
109
ax: the plt.Axes to plot on. If not given, a new figure is created,
102
110
plotted on, and shown.
103
111
**plot_kwargs: Arguments to be passed to 'plt.Axes.plot'.
112
+
113
+ Returns:
114
+ The plt.Axes that was plotted on.
104
115
"""
105
116
show_plot = not ax
106
117
if not isinstance (ax , plt .Axes ):
@@ -143,12 +154,15 @@ def all_errors(self) -> Dict[Tuple['cirq.GridQubit', 'cirq.GridQubit'], float]:
143
154
return {(q0 , q1 ): self .xeb_error (q0 , q1 ) for q0 , q1 in self .all_qubit_pairs }
144
155
145
156
def plot_histogram (self , ax : Optional [plt .Axes ] = None , ** plot_kwargs ) -> plt .Axes :
146
- """plot a histogram of all xeb errors
157
+ """plot a histogram of all xeb errors.
147
158
148
159
Args:
149
160
ax: the plt.Axes to plot on. If not given, a new figure is created,
150
161
plotted on, and shown.
151
162
**plot_kwargs: Arguments to be passed to 'plt.Axes.plot'.
163
+
164
+ Returns:
165
+ The plt.Axes that was plotted on.
152
166
"""
153
167
fig = None
154
168
if ax is None :
@@ -172,7 +186,12 @@ def pauli_error(self) -> Dict[Tuple['cirq.GridQubit', 'cirq.GridQubit'], float]:
172
186
173
187
@dataclass (frozen = True )
174
188
class InferredXEBResult :
175
- """Uses the results from XEB and RB to compute inferred two-qubit Pauli errors."""
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
+ """
176
195
177
196
rb_result : ParallelRandomizedBenchmarkingResult
178
197
xeb_result : TwoQubitXEBResult
@@ -226,6 +245,19 @@ def inferred_xeb_error(self) -> Mapping[Tuple['cirq.GridQubit', 'cirq.GridQubit'
226
245
def _target_errors (
227
246
self , target_error : str
228
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
+ """
229
261
error_funcs = {
230
262
'pauli' : self .inferred_pauli_error ,
231
263
'decay_constant' : self .inferred_decay_constant ,
@@ -270,12 +302,15 @@ def plot_histogram(
270
302
271
303
Args:
272
304
target_error: The error to draw. Must be one of 'xeb', 'pauli', or 'decay_constant'
273
- kind: Whether to plot the single-qubit RB errors ('single_qubit') or the
274
- two-qubit inferred errors ('two_qubit') or both ('both').
275
305
ax: the plt.Axes to plot on. If not given, a new figure is created,
276
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').
277
309
**plot_kwargs: Arguments to be passed to 'plt.Axes.plot'.
278
310
311
+ Returns:
312
+ The plt.Axes that was plotted on.
313
+
279
314
Raises:
280
315
ValueError: If
281
316
- `kind` is not one of 'single_qubit', 'two_qubit', or 'both'.
@@ -320,7 +355,7 @@ def parallel_two_qubit_xeb(
320
355
n_combinations : int = 10 ,
321
356
n_circuits : int = 20 ,
322
357
cycle_depths : Sequence [int ] = tuple (np .arange (3 , 100 , 20 )),
323
- random_state : 'cirq.RANDOM_STATE_OR_SEED_LIKE' = 42 ,
358
+ random_state : 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None ,
324
359
ax : Optional [plt .Axes ] = None ,
325
360
** plot_kwargs ,
326
361
) -> TwoQubitXEBResult :
@@ -386,3 +421,63 @@ def parallel_two_qubit_xeb(
386
421
)
387
422
388
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 )
0 commit comments