Skip to content

Commit 1961207

Browse files
Add parallel randomized benchmarking (#6382)
1 parent 2ce4ed1 commit 1961207

File tree

3 files changed

+105
-14
lines changed

3 files changed

+105
-14
lines changed

cirq-core/cirq/experiments/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
TomographyResult,
2121
two_qubit_randomized_benchmarking,
2222
two_qubit_state_tomography,
23+
parallel_single_qubit_randomized_benchmarking,
2324
)
2425

2526
from cirq.experiments.fidelity_estimation import (

cirq-core/cirq/experiments/qubit_characterizations.py

+89-14
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,18 @@
1515
import dataclasses
1616
import itertools
1717

18-
from typing import Any, cast, Iterator, List, Optional, Sequence, Tuple, TYPE_CHECKING
18+
from typing import (
19+
Any,
20+
cast,
21+
Iterator,
22+
List,
23+
Optional,
24+
Sequence,
25+
Tuple,
26+
TYPE_CHECKING,
27+
Mapping,
28+
Dict,
29+
)
1930
import numpy as np
2031
from scipy.optimize import curve_fit
2132

@@ -207,9 +218,9 @@ def single_qubit_randomized_benchmarking(
207218
qubit: 'cirq.Qid',
208219
use_xy_basis: bool = True,
209220
*,
210-
num_clifford_range: Sequence[int] = range(10, 100, 10),
211-
num_circuits: int = 20,
212-
repetitions: int = 1000,
221+
num_clifford_range: Sequence[int] = tuple(np.logspace(np.log10(5), 3, 5, dtype=int)),
222+
num_circuits: int = 10,
223+
repetitions: int = 600,
213224
) -> RandomizedBenchMarkResult:
214225
"""Clifford-based randomized benchmarking (RB) of a single qubit.
215226
@@ -245,21 +256,75 @@ def single_qubit_randomized_benchmarking(
245256
A RandomizedBenchMarkResult object that stores and plots the result.
246257
"""
247258

259+
qubits = cast(Iterator['cirq.Qid'], (qubit,))
260+
result = parallel_single_qubit_randomized_benchmarking(
261+
sampler,
262+
qubits,
263+
use_xy_basis,
264+
num_clifford_range=num_clifford_range,
265+
num_circuits=num_circuits,
266+
repetitions=repetitions,
267+
)
268+
return result[qubit]
269+
270+
271+
def parallel_single_qubit_randomized_benchmarking(
272+
sampler: 'cirq.Sampler',
273+
qubits: Iterator['cirq.Qid'],
274+
use_xy_basis: bool = True,
275+
*,
276+
num_clifford_range: Sequence[int] = tuple(
277+
np.logspace(np.log10(5), np.log10(1000), 5, dtype=int)
278+
),
279+
num_circuits: int = 10,
280+
repetitions: int = 1000,
281+
) -> Mapping['cirq.Qid', 'RandomizedBenchMarkResult']:
282+
"""Clifford-based randomized benchmarking (RB) single qubits in parallel.
283+
284+
This is the same as `single_qubit_randomized_benchmarking` except on all
285+
of the specified qubits in parallel, i.e. with the individual randomized
286+
benchmarking circuits zipped together.
287+
288+
Args:
289+
sampler: The quantum engine or simulator to run the circuits.
290+
use_xy_basis: Determines if the Clifford gates are built with x and y
291+
rotations (True) or x and z rotations (False).
292+
qubits: The qubits to benchmark.
293+
num_clifford_range: The different numbers of Cliffords in the RB study.
294+
num_circuits: The number of random circuits generated for each
295+
number of Cliffords.
296+
repetitions: The number of repetitions of each circuit.
297+
298+
Returns:
299+
A dictionary from qubits to RandomizedBenchMarkResult objects.
300+
"""
301+
248302
cliffords = _single_qubit_cliffords()
249303
c1 = cliffords.c1_in_xy if use_xy_basis else cliffords.c1_in_xz
250-
cfd_mats = np.array([_gate_seq_to_mats(gates) for gates in c1])
304+
clifford_mats = np.array([_gate_seq_to_mats(gates) for gates in c1])
251305

252-
gnd_probs = []
253-
for num_cfds in num_clifford_range:
254-
excited_probs_l = []
306+
# create circuits
307+
circuits_all: List['cirq.AbstractCircuit'] = []
308+
for num_cliffords in num_clifford_range:
255309
for _ in range(num_circuits):
256-
circuit = _random_single_q_clifford(qubit, num_cfds, c1, cfd_mats)
257-
circuit.append(ops.measure(qubit, key='z'))
258-
results = sampler.run(circuit, repetitions=repetitions)
259-
excited_probs_l.append(np.mean(results.measurements['z']))
260-
gnd_probs.append(1.0 - np.mean(excited_probs_l))
310+
circuits_all.append(
311+
_create_parallel_rb_circuit(qubits, num_cliffords, c1, clifford_mats)
312+
)
261313

262-
return RandomizedBenchMarkResult(num_clifford_range, gnd_probs)
314+
# run circuits
315+
results = sampler.run_batch(circuits_all, repetitions=repetitions)
316+
gnd_probs: dict = {q: [] for q in qubits}
317+
idx = 0
318+
for num_cliffords in num_clifford_range:
319+
excited_probs: Dict['cirq.Qid', List[float]] = {q: [] for q in qubits}
320+
for _ in range(num_circuits):
321+
result = results[idx][0]
322+
for qubit in qubits:
323+
excited_probs[qubit].append(np.mean(result.measurements[str(qubit)]))
324+
idx += 1
325+
for qubit in qubits:
326+
gnd_probs[qubit].append(1.0 - np.mean(excited_probs[qubit]))
327+
return {q: RandomizedBenchMarkResult(num_clifford_range, gnd_probs[q]) for q in qubits}
263328

264329

265330
def two_qubit_randomized_benchmarking(
@@ -496,6 +561,16 @@ def _measurement(two_qubit_circuit: circuits.Circuit) -> np.ndarray:
496561
return TomographyResult(rho)
497562

498563

564+
def _create_parallel_rb_circuit(
565+
qubits: Iterator['cirq.Qid'], num_cliffords: int, c1: list, clifford_mats: np.ndarray
566+
) -> 'cirq.Circuit':
567+
circuits_to_zip = [
568+
_random_single_q_clifford(qubit, num_cliffords, c1, clifford_mats) for qubit in qubits
569+
]
570+
circuit = circuits.Circuit.zip(*circuits_to_zip)
571+
return circuits.Circuit.from_moments(*circuit, ops.measure_each(*qubits))
572+
573+
499574
def _indices_after_basis_rot(i: int, j: int) -> Tuple[int, Sequence[int], Sequence[int]]:
500575
mat_idx = 3 * (3 * i + j)
501576
q_0_i = 3 - i

cirq-core/cirq/experiments/qubit_characterizations_test.py

+15
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
two_qubit_randomized_benchmarking,
2727
single_qubit_state_tomography,
2828
two_qubit_state_tomography,
29+
parallel_single_qubit_randomized_benchmarking,
2930
)
3031

3132

@@ -92,6 +93,20 @@ def test_single_qubit_randomized_benchmarking():
9293
assert np.isclose(results.pauli_error(), 0.0, atol=1e-7) # warning is expected
9394

9495

96+
def test_parallel_single_qubit_randomized_benchmarking():
97+
# Check that the ground state population at the end of the Clifford
98+
# sequences is always unity.
99+
simulator = sim.Simulator()
100+
qubits = (GridQubit(0, 0), GridQubit(0, 1))
101+
num_cfds = range(5, 20, 5)
102+
results = parallel_single_qubit_randomized_benchmarking(
103+
simulator, num_clifford_range=num_cfds, repetitions=100, qubits=qubits
104+
)
105+
for qubit in qubits:
106+
g_pops = np.asarray(results[qubit].data)[:, 1]
107+
assert np.isclose(np.mean(g_pops), 1.0)
108+
109+
95110
def test_two_qubit_randomized_benchmarking():
96111
# Check that the ground state population at the end of the Clifford
97112
# sequences is always unity.

0 commit comments

Comments
 (0)