Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
76 changes: 76 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,70 @@ 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',
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.

*,
qubits: Optional[Iterator['cirq.GridQubit']] = None,
num_clifford_range: Sequence[int] = [5, 18, 70, 265, 1000],
Comment thread
eliottrosenberg marked this conversation as resolved.
Outdated
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. If None, benchmark all of the qubits.
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.
"""

if qubits is None: # pragma: no cover
try:
Comment thread
eliottrosenberg marked this conversation as resolved.
Outdated
device = sampler.processor.get_device() # type: ignore
Comment thread
eliottrosenberg marked this conversation as resolved.
Outdated
except:
device = sampler.device # type: ignore
qubits = device.metadata.qubit_set

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])
Comment thread
eliottrosenberg marked this conversation as resolved.
Outdated

# 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_cfds in num_clifford_range:
for _ in range(num_circuits):
circuits_all.append(_create_parallel_rb_circuit(qubits, num_cfds, c1, cfd_mats))
Comment thread
NoureldinYosri marked this conversation as resolved.
Outdated

# 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_cfds 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[f"q{qubit.row}_{qubit.col}"])
Comment thread
eliottrosenberg marked this conversation as resolved.
Outdated
)
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 +528,18 @@ def _measurement(two_qubit_circuit: circuits.Circuit) -> np.ndarray:
return TomographyResult(rho)


def _create_parallel_rb_circuit(
qubits: Iterator['cirq.GridQubit'], num_cfds: int, c1: list, cfd_mats: np.ndarray
) -> 'cirq.Circuit':
circuits_to_zip = [_random_single_q_clifford(qubit, num_cfds, c1, cfd_mats) for qubit in qubits]
circuit = circuits.Circuit.zip(*circuits_to_zip)
measure_moment = circuits.Moment(
ops.measure_each(*qubits, key_func=lambda q: f"q{q.row}_{q.col}") # type: ignore
Comment thread
eliottrosenberg marked this conversation as resolved.
Outdated
)
circuit_with_meas = circuits.Circuit.from_moments(*(list(circuit.moments) + [measure_moment]))
Comment thread
eliottrosenberg marked this conversation as resolved.
Outdated
return circuit_with_meas


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