Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
69 changes: 69 additions & 0 deletions cirq-core/cirq/experiments/qubit_characterizations.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,63 @@ def single_qubit_randomized_benchmarking(
return RandomizedBenchMarkResult(num_clifford_range, gnd_probs)


def parallel_single_qubit_randomized_benchmarking(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OOC, would it be possible to put this in the existing single_qubit_randomized_benchmarking module? Seems like it's basically the same thing and there could be opportunities for code reuse. The fact that one is running in parallel on multiple qubits is a minor details (the existing single-qubit code could just become a special case of the parallel code).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I changed it so that single_qubit_randomized_benchmarking is now a wrapper for parallel_single_qubit_randomized_benchmarking. What do you think?

sampler: 'cirq.Sampler',
qubits: Iterator['cirq.Qid'],
use_xy_basis: bool = True,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason this is before the kwargs cut-off?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was following the same format as in single_qubit_randomized_benchmarking() in the same file, but I can change it.

*,
num_clifford_range: Sequence[int] = tuple(np.logspace(np.log10(5), 3, 5, dtype=int)),
num_circuits: int = 10,
repetitions: int = 600,
) -> dict:
Comment thread
eliottrosenberg marked this conversation as resolved.
Outdated
"""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
clifford_mats = np.array([_gate_seq_to_mats(gates) for gates in c1])

# create circuits
circuits_all = []
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: call this circuits and add a type annotation

Suggested change
circuits_all = []
circuits: list[`cirq.AbstractCircuit`] = []

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think I should call this circuits because earlier in the file, there is from cirq import circuits.

for num_cliffords in num_clifford_range:
for _ in range(num_circuits):
circuits_all.append(
_create_parallel_rb_circuit(qubits, num_cliffords, c1, clifford_mats)
)

# run circuits
results_all = sampler.run_batch(circuits_all, repetitions=repetitions)
Comment thread
eliottrosenberg marked this conversation as resolved.
Outdated
gnd_probs: dict = {q: [] for q in qubits}
idx = 0
for num_cliffords in num_clifford_range:
excited_probs_l: dict = {q: [] for q in qubits}
Comment thread
eliottrosenberg marked this conversation as resolved.
Outdated
for _ in range(num_circuits):
results = results_all[idx][0]
for qubit in qubits:
excited_probs_l[qubit].append(np.mean(results.measurements[str(qubit)]))
idx += 1
for qubit in qubits:
gnd_probs[qubit].append(1.0 - np.mean(excited_probs_l[qubit]))
return {q: RandomizedBenchMarkResult(num_clifford_range, gnd_probs[q]) for q in qubits}


def two_qubit_randomized_benchmarking(
sampler: 'cirq.Sampler',
first_qubit: 'cirq.Qid',
Expand Down Expand Up @@ -464,6 +521,18 @@ 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)
measure_moment = circuits.Moment(ops.measure_each(*qubits))
circuit_with_meas = circuits.Circuit.from_moments(*circuit.moments, measure_moment)
return circuit_with_meas
Comment thread
eliottrosenberg marked this conversation as resolved.
Outdated


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 @@ -93,6 +94,20 @@ def test_single_qubit_randomized_benchmarking():
assert np.isclose(np.mean(g_pops), 1.0)


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