Skip to content

Commit

Permalink
Add parallel randomized benchmarking (#6382)
Browse files Browse the repository at this point in the history
  • Loading branch information
eliottrosenberg authored Dec 19, 2023
1 parent 2ce4ed1 commit 1961207
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 14 deletions.
1 change: 1 addition & 0 deletions cirq-core/cirq/experiments/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
TomographyResult,
two_qubit_randomized_benchmarking,
two_qubit_state_tomography,
parallel_single_qubit_randomized_benchmarking,
)

from cirq.experiments.fidelity_estimation import (
Expand Down
103 changes: 89 additions & 14 deletions cirq-core/cirq/experiments/qubit_characterizations.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,18 @@
import dataclasses
import itertools

from typing import Any, cast, Iterator, List, Optional, Sequence, Tuple, TYPE_CHECKING
from typing import (
Any,
cast,
Iterator,
List,
Optional,
Sequence,
Tuple,
TYPE_CHECKING,
Mapping,
Dict,
)
import numpy as np
from scipy.optimize import curve_fit

Expand Down Expand Up @@ -207,9 +218,9 @@ def single_qubit_randomized_benchmarking(
qubit: 'cirq.Qid',
use_xy_basis: bool = True,
*,
num_clifford_range: Sequence[int] = range(10, 100, 10),
num_circuits: int = 20,
repetitions: int = 1000,
num_clifford_range: Sequence[int] = tuple(np.logspace(np.log10(5), 3, 5, dtype=int)),
num_circuits: int = 10,
repetitions: int = 600,
) -> RandomizedBenchMarkResult:
"""Clifford-based randomized benchmarking (RB) of a single qubit.
Expand Down Expand Up @@ -245,21 +256,75 @@ def single_qubit_randomized_benchmarking(
A RandomizedBenchMarkResult object that stores and plots the result.
"""

qubits = cast(Iterator['cirq.Qid'], (qubit,))
result = parallel_single_qubit_randomized_benchmarking(
sampler,
qubits,
use_xy_basis,
num_clifford_range=num_clifford_range,
num_circuits=num_circuits,
repetitions=repetitions,
)
return result[qubit]


def parallel_single_qubit_randomized_benchmarking(
sampler: 'cirq.Sampler',
qubits: Iterator['cirq.Qid'],
use_xy_basis: bool = True,
*,
num_clifford_range: Sequence[int] = tuple(
np.logspace(np.log10(5), np.log10(1000), 5, dtype=int)
),
num_circuits: int = 10,
repetitions: int = 1000,
) -> Mapping['cirq.Qid', 'RandomizedBenchMarkResult']:
"""Clifford-based randomized benchmarking (RB) single qubits in parallel.
This is the same as `single_qubit_randomized_benchmarking` except on all
of the specified qubits in parallel, i.e. with the individual randomized
benchmarking circuits zipped together.
Args:
sampler: The quantum engine or simulator to run the circuits.
use_xy_basis: Determines if the Clifford gates are built with x and y
rotations (True) or x and z rotations (False).
qubits: The qubits to benchmark.
num_clifford_range: The different numbers of Cliffords in the RB study.
num_circuits: The number of random circuits generated for each
number of Cliffords.
repetitions: The number of repetitions of each circuit.
Returns:
A dictionary from qubits to RandomizedBenchMarkResult objects.
"""

cliffords = _single_qubit_cliffords()
c1 = cliffords.c1_in_xy if use_xy_basis else cliffords.c1_in_xz
cfd_mats = np.array([_gate_seq_to_mats(gates) for gates in c1])
clifford_mats = np.array([_gate_seq_to_mats(gates) for gates in c1])

gnd_probs = []
for num_cfds in num_clifford_range:
excited_probs_l = []
# create circuits
circuits_all: List['cirq.AbstractCircuit'] = []
for num_cliffords in num_clifford_range:
for _ in range(num_circuits):
circuit = _random_single_q_clifford(qubit, num_cfds, c1, cfd_mats)
circuit.append(ops.measure(qubit, key='z'))
results = sampler.run(circuit, repetitions=repetitions)
excited_probs_l.append(np.mean(results.measurements['z']))
gnd_probs.append(1.0 - np.mean(excited_probs_l))
circuits_all.append(
_create_parallel_rb_circuit(qubits, num_cliffords, c1, clifford_mats)
)

return RandomizedBenchMarkResult(num_clifford_range, gnd_probs)
# run circuits
results = sampler.run_batch(circuits_all, repetitions=repetitions)
gnd_probs: dict = {q: [] for q in qubits}
idx = 0
for num_cliffords in num_clifford_range:
excited_probs: Dict['cirq.Qid', List[float]] = {q: [] for q in qubits}
for _ in range(num_circuits):
result = results[idx][0]
for qubit in qubits:
excited_probs[qubit].append(np.mean(result.measurements[str(qubit)]))
idx += 1
for qubit in qubits:
gnd_probs[qubit].append(1.0 - np.mean(excited_probs[qubit]))
return {q: RandomizedBenchMarkResult(num_clifford_range, gnd_probs[q]) for q in qubits}


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


def _create_parallel_rb_circuit(
qubits: Iterator['cirq.Qid'], num_cliffords: int, c1: list, clifford_mats: np.ndarray
) -> 'cirq.Circuit':
circuits_to_zip = [
_random_single_q_clifford(qubit, num_cliffords, c1, clifford_mats) for qubit in qubits
]
circuit = circuits.Circuit.zip(*circuits_to_zip)
return circuits.Circuit.from_moments(*circuit, ops.measure_each(*qubits))


def _indices_after_basis_rot(i: int, j: int) -> Tuple[int, Sequence[int], Sequence[int]]:
mat_idx = 3 * (3 * i + j)
q_0_i = 3 - i
Expand Down
15 changes: 15 additions & 0 deletions cirq-core/cirq/experiments/qubit_characterizations_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
two_qubit_randomized_benchmarking,
single_qubit_state_tomography,
two_qubit_state_tomography,
parallel_single_qubit_randomized_benchmarking,
)


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


def test_parallel_single_qubit_randomized_benchmarking():
# Check that the ground state population at the end of the Clifford
# sequences is always unity.
simulator = sim.Simulator()
qubits = (GridQubit(0, 0), GridQubit(0, 1))
num_cfds = range(5, 20, 5)
results = parallel_single_qubit_randomized_benchmarking(
simulator, num_clifford_range=num_cfds, repetitions=100, qubits=qubits
)
for qubit in qubits:
g_pops = np.asarray(results[qubit].data)[:, 1]
assert np.isclose(np.mean(g_pops), 1.0)


def test_two_qubit_randomized_benchmarking():
# Check that the ground state population at the end of the Clifford
# sequences is always unity.
Expand Down

0 comments on commit 1961207

Please sign in to comment.