From 895d9fb3c134aa1fee4365e683e324ede708c08d Mon Sep 17 00:00:00 2001 From: Nour Yosri Date: Wed, 4 Jun 2025 13:39:08 -0700 Subject: [PATCH 01/20] Update RB to reduce the size of the generated circuits --- .../experiments/qubit_characterizations.py | 37 ++++++++++++++++--- .../qubit_characterizations_test.py | 9 +++-- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/cirq-core/cirq/experiments/qubit_characterizations.py b/cirq-core/cirq/experiments/qubit_characterizations.py index 5dbc22e9a6e..568e8b1436d 100644 --- a/cirq-core/cirq/experiments/qubit_characterizations.py +++ b/cirq-core/cirq/experiments/qubit_characterizations.py @@ -19,6 +19,7 @@ import itertools from typing import Any, cast, Iterator, Mapping, Sequence, TYPE_CHECKING +import attrs import numpy as np from matplotlib import pyplot as plt @@ -36,7 +37,31 @@ import cirq -@dataclasses.dataclass +def _canonize_clifford_sequences( + sequences: list[list[ops.SingleQubitCliffordGate]], +) -> list[list[ops.SingleQubitCliffordGate]]: + return [[_reduce_gate_seq(seq)] for seq in sequences] + + +@attrs.frozen +class _CliffordGateSequence: + gate_sequence: list[list[ops.SingleQubitCliffordGate]] + + @functools.cached_property + def _reduced_gate_sequence(self): + return _canonize_clifford_sequences(self.gate_sequence) + + def __iter__(self): + yield from self._reduced_gate_sequence + + def __getitem__(self, idx): + return self._reduced_gate_sequence[idx] + + def __len__(self): + return len(self._reduced_gate_sequence) + + +@attrs.frozen class Cliffords: """The single-qubit Clifford group, decomposed into elementary gates. @@ -54,11 +79,11 @@ class Cliffords: s1_y """ - c1_in_xy: list[list[ops.SingleQubitCliffordGate]] - c1_in_xz: list[list[ops.SingleQubitCliffordGate]] - s1: list[list[ops.SingleQubitCliffordGate]] - s1_x: list[list[ops.SingleQubitCliffordGate]] - s1_y: list[list[ops.SingleQubitCliffordGate]] + c1_in_xy: _CliffordGateSequence = attrs.field(converter=_CliffordGateSequence) + c1_in_xz: _CliffordGateSequence = attrs.field(converter=_CliffordGateSequence) + s1: _CliffordGateSequence = attrs.field(converter=_CliffordGateSequence) + s1_x: _CliffordGateSequence = attrs.field(converter=_CliffordGateSequence) + s1_y: _CliffordGateSequence = attrs.field(converter=_CliffordGateSequence) class RandomizedBenchMarkResult: diff --git a/cirq-core/cirq/experiments/qubit_characterizations_test.py b/cirq-core/cirq/experiments/qubit_characterizations_test.py index 6e792c16477..8f6a5e3f4cc 100644 --- a/cirq-core/cirq/experiments/qubit_characterizations_test.py +++ b/cirq-core/cirq/experiments/qubit_characterizations_test.py @@ -74,7 +74,7 @@ def check_distinct(unitaries): assert is_pauli(u @ p @ u.conj().T), str(u) # Check that XZ decomposition has at most one X gate per clifford. - for gates in cliffords.c1_in_xz: + for gates in cliffords.c1_in_xz.gate_sequence: num_i = len([gate for gate in gates if gate == cirq.ops.SingleQubitCliffordGate.I]) num_x = len( [ @@ -229,13 +229,16 @@ def test_tomography_plot_raises_for_incorrect_number_of_axes(): result.plot(axes) -def test_single_qubit_cliffords_gateset(): +@pytest.mark.parametrize('num_cliffords', range(5, 10)) +def test_single_qubit_cliffords_gateset(num_cliffords): qubits = [GridQubit(0, i) for i in range(4)] clifford_group = cirq.experiments.qubit_characterizations._single_qubit_cliffords() c = cirq.experiments.qubit_characterizations._create_parallel_rb_circuit( - qubits, 5, clifford_group.c1_in_xy + qubits, num_cliffords, clifford_group.c1_in_xy ) device = cirq.testing.ValidatingTestDevice( qubits=qubits, allowed_gates=(cirq.ops.PhasedXZGate, cirq.MeasurementGate) ) device.validate_circuit(c) + + assert len(c) == num_cliffords + 2 From 266cfcd1d399ed703b8ad7ef57a8a0a3470839cd Mon Sep 17 00:00:00 2001 From: Nour Yosri Date: Wed, 4 Jun 2025 13:45:02 -0700 Subject: [PATCH 02/20] mypy --- cirq-core/cirq/experiments/qubit_characterizations.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cirq-core/cirq/experiments/qubit_characterizations.py b/cirq-core/cirq/experiments/qubit_characterizations.py index 568e8b1436d..43f7d3d0e18 100644 --- a/cirq-core/cirq/experiments/qubit_characterizations.py +++ b/cirq-core/cirq/experiments/qubit_characterizations.py @@ -702,7 +702,9 @@ def _measurement(two_qubit_circuit: circuits.Circuit) -> np.ndarray: def _create_parallel_rb_circuit( - qubits: Sequence[cirq.Qid], num_cliffords: int, c1: list + qubits: Sequence[cirq.Qid], + num_cliffords: int, + c1: _CliffordGateSequence | list[list[ops.SingleQubitCliffordGate]], ) -> cirq.Circuit: sequences_to_zip = [_random_single_q_clifford(qubit, num_cliffords, c1) for qubit in qubits] # Ensure each sequence has the same number of moments. From 401449e869c359a549b0cb16d12c80df1108ddc8 Mon Sep 17 00:00:00 2001 From: Nour Yosri Date: Wed, 4 Jun 2025 14:34:04 -0700 Subject: [PATCH 03/20] mypy --- cirq-core/cirq/experiments/qubit_characterizations.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cirq-core/cirq/experiments/qubit_characterizations.py b/cirq-core/cirq/experiments/qubit_characterizations.py index 43f7d3d0e18..e40e1e32171 100644 --- a/cirq-core/cirq/experiments/qubit_characterizations.py +++ b/cirq-core/cirq/experiments/qubit_characterizations.py @@ -757,7 +757,9 @@ def _two_qubit_clifford_matrices(q_0: cirq.Qid, q_1: cirq.Qid, cliffords: Cliffo def _random_single_q_clifford( - qubit: cirq.Qid, num_cfds: int, cfds: Sequence[Sequence[cirq.ops.SingleQubitCliffordGate]] + qubit: cirq.Qid, + num_cfds: int, + cfds: Sequence[Sequence[cirq.ops.SingleQubitCliffordGate]] | _single_qubit_cliffords, ) -> list[cirq.Operation]: clifford_group_size = 24 operations = [[gate.to_phased_xz_gate()(qubit) for gate in gates] for gates in cfds] From 04847957286538fb66ccc3f264b2c49eaa447525 Mon Sep 17 00:00:00 2001 From: Nour Yosri Date: Wed, 4 Jun 2025 14:50:21 -0700 Subject: [PATCH 04/20] mypy --- cirq-core/cirq/experiments/qubit_characterizations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cirq-core/cirq/experiments/qubit_characterizations.py b/cirq-core/cirq/experiments/qubit_characterizations.py index e40e1e32171..e57b40828da 100644 --- a/cirq-core/cirq/experiments/qubit_characterizations.py +++ b/cirq-core/cirq/experiments/qubit_characterizations.py @@ -759,7 +759,7 @@ def _two_qubit_clifford_matrices(q_0: cirq.Qid, q_1: cirq.Qid, cliffords: Cliffo def _random_single_q_clifford( qubit: cirq.Qid, num_cfds: int, - cfds: Sequence[Sequence[cirq.ops.SingleQubitCliffordGate]] | _single_qubit_cliffords, + cfds: Sequence[Sequence[cirq.ops.SingleQubitCliffordGate]] | _CliffordGateSequence, ) -> list[cirq.Operation]: clifford_group_size = 24 operations = [[gate.to_phased_xz_gate()(qubit) for gate in gates] for gates in cfds] From cfcb3d69aff556172458e37db77200d1c16ce6cb Mon Sep 17 00:00:00 2001 From: Nour Yosri Date: Thu, 5 Jun 2025 09:38:33 -0700 Subject: [PATCH 05/20] change default value of use_xy_basis and add docstring --- .../experiments/qubit_characterizations.py | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/cirq-core/cirq/experiments/qubit_characterizations.py b/cirq-core/cirq/experiments/qubit_characterizations.py index e57b40828da..00c580bd244 100644 --- a/cirq-core/cirq/experiments/qubit_characterizations.py +++ b/cirq-core/cirq/experiments/qubit_characterizations.py @@ -39,25 +39,31 @@ def _canonize_clifford_sequences( sequences: list[list[ops.SingleQubitCliffordGate]], -) -> list[list[ops.SingleQubitCliffordGate]]: - return [[_reduce_gate_seq(seq)] for seq in sequences] +) -> list[tuple[ops.SingleQubitCliffordGate]]: + return [(_reduce_gate_seq(seq),) for seq in sequences] @attrs.frozen class _CliffordGateSequence: + """Wrap around a list of sequences of clifford gates. + + This class wraps around a list of sequences of clifford gates and re-exposes them as + a list of tuple where each tuple contains a single clifford gates. + """ + gate_sequence: list[list[ops.SingleQubitCliffordGate]] @functools.cached_property - def _reduced_gate_sequence(self): + def _reduced_gate_sequence(self) -> list[tuple[ops.SingleQubitCliffordGate]]: return _canonize_clifford_sequences(self.gate_sequence) - def __iter__(self): + def __iter__(self) -> Iterator[tuple[ops.SingleQubitCliffordGate]]: yield from self._reduced_gate_sequence - def __getitem__(self, idx): + def __getitem__(self, idx) -> tuple[ops.SingleQubitCliffordGate]: return self._reduced_gate_sequence[idx] - def __len__(self): + def __len__(self) -> int: return len(self._reduced_gate_sequence) @@ -415,7 +421,7 @@ def single_qubit_randomized_benchmarking( def parallel_single_qubit_randomized_benchmarking( sampler: cirq.Sampler, qubits: Sequence[cirq.Qid], - use_xy_basis: bool = True, + use_xy_basis: bool = False, *, num_clifford_range: Sequence[int] = tuple( np.logspace(np.log10(5), np.log10(1000), 5, dtype=int) From 39ba4b4a7d9b4376c093fbe8752f04d5f0e0416f Mon Sep 17 00:00:00 2001 From: Nour Yosri Date: Thu, 5 Jun 2025 09:40:19 -0700 Subject: [PATCH 06/20] nit --- cirq-core/cirq/experiments/qubit_characterizations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cirq-core/cirq/experiments/qubit_characterizations.py b/cirq-core/cirq/experiments/qubit_characterizations.py index 00c580bd244..4ee9c971d87 100644 --- a/cirq-core/cirq/experiments/qubit_characterizations.py +++ b/cirq-core/cirq/experiments/qubit_characterizations.py @@ -367,7 +367,7 @@ def plot( def single_qubit_randomized_benchmarking( sampler: cirq.Sampler, qubit: cirq.Qid, - use_xy_basis: bool = True, + use_xy_basis: bool = False, *, num_clifford_range: Sequence[int] = tuple(np.logspace(np.log10(5), 3, 5, dtype=int)), num_circuits: int = 10, From 1b59784650a93ab053ed17551da873abfaac901e Mon Sep 17 00:00:00 2001 From: Nour Yosri Date: Wed, 25 Jun 2025 18:40:49 -0700 Subject: [PATCH 07/20] add option to remove half turns --- .../experiments/qubit_characterizations.py | 74 ++++++++----------- .../qubit_characterizations_test.py | 13 +++- 2 files changed, 39 insertions(+), 48 deletions(-) diff --git a/cirq-core/cirq/experiments/qubit_characterizations.py b/cirq-core/cirq/experiments/qubit_characterizations.py index 4ee9c971d87..793c50ea343 100644 --- a/cirq-core/cirq/experiments/qubit_characterizations.py +++ b/cirq-core/cirq/experiments/qubit_characterizations.py @@ -19,7 +19,6 @@ import itertools from typing import Any, cast, Iterator, Mapping, Sequence, TYPE_CHECKING -import attrs import numpy as np from matplotlib import pyplot as plt @@ -39,35 +38,11 @@ def _canonize_clifford_sequences( sequences: list[list[ops.SingleQubitCliffordGate]], -) -> list[tuple[ops.SingleQubitCliffordGate]]: - return [(_reduce_gate_seq(seq),) for seq in sequences] +) -> list[list[ops.SingleQubitCliffordGate]]: + return [[_reduce_gate_seq(seq)] for seq in sequences] -@attrs.frozen -class _CliffordGateSequence: - """Wrap around a list of sequences of clifford gates. - - This class wraps around a list of sequences of clifford gates and re-exposes them as - a list of tuple where each tuple contains a single clifford gates. - """ - - gate_sequence: list[list[ops.SingleQubitCliffordGate]] - - @functools.cached_property - def _reduced_gate_sequence(self) -> list[tuple[ops.SingleQubitCliffordGate]]: - return _canonize_clifford_sequences(self.gate_sequence) - - def __iter__(self) -> Iterator[tuple[ops.SingleQubitCliffordGate]]: - yield from self._reduced_gate_sequence - - def __getitem__(self, idx) -> tuple[ops.SingleQubitCliffordGate]: - return self._reduced_gate_sequence[idx] - - def __len__(self) -> int: - return len(self._reduced_gate_sequence) - - -@attrs.frozen +@dataclasses.dataclass class Cliffords: """The single-qubit Clifford group, decomposed into elementary gates. @@ -83,13 +58,14 @@ class Cliffords: s1 s1_x s1_y + """ - c1_in_xy: _CliffordGateSequence = attrs.field(converter=_CliffordGateSequence) - c1_in_xz: _CliffordGateSequence = attrs.field(converter=_CliffordGateSequence) - s1: _CliffordGateSequence = attrs.field(converter=_CliffordGateSequence) - s1_x: _CliffordGateSequence = attrs.field(converter=_CliffordGateSequence) - s1_y: _CliffordGateSequence = attrs.field(converter=_CliffordGateSequence) + c1_in_xy: list[list[ops.SingleQubitCliffordGate]] + c1_in_xz: list[list[ops.SingleQubitCliffordGate]] + s1: list[list[ops.SingleQubitCliffordGate]] + s1_x: list[list[ops.SingleQubitCliffordGate]] + s1_y: list[list[ops.SingleQubitCliffordGate]] class RandomizedBenchMarkResult: @@ -418,6 +394,17 @@ def single_qubit_randomized_benchmarking( return result.results_dictionary[qubit] +def _gateset_selector( + use_xy_basis: bool, xy_only: bool, xz_only: bool +) -> list[list[ops.SingleQubitCliffordGate]]: + clifford_group = _single_qubit_cliffords() + sequences = clifford_group.c1_in_xy if use_xy_basis else clifford_group.c1_in_xz + filter_seq = lambda seq: len(seq) == 2 + if xy_only or xz_only: + sequences = list(filter(filter_seq, sequences)) + return _canonize_clifford_sequences(sequences) + + def parallel_single_qubit_randomized_benchmarking( sampler: cirq.Sampler, qubits: Sequence[cirq.Qid], @@ -428,6 +415,8 @@ def parallel_single_qubit_randomized_benchmarking( ), num_circuits: int = 10, repetitions: int = 1000, + xy_only: bool = False, + xz_only: bool = False, ) -> ParallelRandomizedBenchmarkingResult: """Clifford-based randomized benchmarking (RB) single qubits in parallel. @@ -444,13 +433,15 @@ def parallel_single_qubit_randomized_benchmarking( num_circuits: The number of random circuits generated for each number of Cliffords. repetitions: The number of repetitions of each circuit. - + xy_only: whether to use only gates that have either an X or a Y component. + if True, this excludes I, Z, Z/2, -Z/2. + xy_only: whether to use only gates that have either an X or a Z component. + if True, this excludes I, Y, Y/2, -Y/2. Returns: A dictionary from qubits to RandomizedBenchMarkResult objects. """ - clifford_group = _single_qubit_cliffords() - c1 = clifford_group.c1_in_xy if use_xy_basis else clifford_group.c1_in_xz + c1 = _gateset_selector(use_xy_basis, xy_only, xz_only) # create circuits circuits_all: list[cirq.AbstractCircuit] = [] @@ -708,9 +699,7 @@ def _measurement(two_qubit_circuit: circuits.Circuit) -> np.ndarray: def _create_parallel_rb_circuit( - qubits: Sequence[cirq.Qid], - num_cliffords: int, - c1: _CliffordGateSequence | list[list[ops.SingleQubitCliffordGate]], + qubits: Sequence[cirq.Qid], num_cliffords: int, c1: list[list[ops.SingleQubitCliffordGate]] ) -> cirq.Circuit: sequences_to_zip = [_random_single_q_clifford(qubit, num_cliffords, c1) for qubit in qubits] # Ensure each sequence has the same number of moments. @@ -763,13 +752,10 @@ def _two_qubit_clifford_matrices(q_0: cirq.Qid, q_1: cirq.Qid, cliffords: Cliffo def _random_single_q_clifford( - qubit: cirq.Qid, - num_cfds: int, - cfds: Sequence[Sequence[cirq.ops.SingleQubitCliffordGate]] | _CliffordGateSequence, + qubit: cirq.Qid, num_cfds: int, cfds: Sequence[Sequence[cirq.ops.SingleQubitCliffordGate]] ) -> list[cirq.Operation]: - clifford_group_size = 24 operations = [[gate.to_phased_xz_gate()(qubit) for gate in gates] for gates in cfds] - gate_ids = np.random.choice(clifford_group_size, num_cfds).tolist() + gate_ids = np.random.choice(len(cfds), num_cfds).tolist() adjoint = _reduce_gate_seq([gate for gate_id in gate_ids for gate in cfds[gate_id]]) ** -1 return [op for gate_id in gate_ids for op in operations[gate_id]] + [ adjoint.to_phased_xz_gate()(qubit) diff --git a/cirq-core/cirq/experiments/qubit_characterizations_test.py b/cirq-core/cirq/experiments/qubit_characterizations_test.py index 8f6a5e3f4cc..05a548a588d 100644 --- a/cirq-core/cirq/experiments/qubit_characterizations_test.py +++ b/cirq-core/cirq/experiments/qubit_characterizations_test.py @@ -74,7 +74,7 @@ def check_distinct(unitaries): assert is_pauli(u @ p @ u.conj().T), str(u) # Check that XZ decomposition has at most one X gate per clifford. - for gates in cliffords.c1_in_xz.gate_sequence: + for gates in cliffords.c1_in_xz: num_i = len([gate for gate in gates if gate == cirq.ops.SingleQubitCliffordGate.I]) num_x = len( [ @@ -230,11 +230,16 @@ def test_tomography_plot_raises_for_incorrect_number_of_axes(): @pytest.mark.parametrize('num_cliffords', range(5, 10)) -def test_single_qubit_cliffords_gateset(num_cliffords): +@pytest.mark.parametrize('use_xy_basis', [False, True]) +@pytest.mark.parametrize('xy_only', [False, True]) +@pytest.mark.parametrize('xz_only', [False, True]) +def test_single_qubit_cliffords_gateset(num_cliffords, use_xy_basis, xy_only, xz_only): qubits = [GridQubit(0, i) for i in range(4)] - clifford_group = cirq.experiments.qubit_characterizations._single_qubit_cliffords() + c1_in_xy = cirq.experiments.qubit_characterizations._gateset_selector( + use_xy_basis, xy_only, xz_only + ) c = cirq.experiments.qubit_characterizations._create_parallel_rb_circuit( - qubits, num_cliffords, clifford_group.c1_in_xy + qubits, num_cliffords, c1_in_xy ) device = cirq.testing.ValidatingTestDevice( qubits=qubits, allowed_gates=(cirq.ops.PhasedXZGate, cirq.MeasurementGate) From 478b401d454873e479dc4a7507538d08087c862a Mon Sep 17 00:00:00 2001 From: Nour Yosri Date: Wed, 25 Jun 2025 18:42:30 -0700 Subject: [PATCH 08/20] nit --- cirq-core/cirq/experiments/qubit_characterizations.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cirq-core/cirq/experiments/qubit_characterizations.py b/cirq-core/cirq/experiments/qubit_characterizations.py index 793c50ea343..bb9900724f6 100644 --- a/cirq-core/cirq/experiments/qubit_characterizations.py +++ b/cirq-core/cirq/experiments/qubit_characterizations.py @@ -58,7 +58,6 @@ class Cliffords: s1 s1_x s1_y - """ c1_in_xy: list[list[ops.SingleQubitCliffordGate]] From 78e76751449a08b7aafd9d54111587f6bded565b Mon Sep 17 00:00:00 2001 From: Noureldin Date: Wed, 25 Jun 2025 20:24:49 -0700 Subject: [PATCH 09/20] Update qubit_characterizations.py --- cirq-core/cirq/experiments/qubit_characterizations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cirq-core/cirq/experiments/qubit_characterizations.py b/cirq-core/cirq/experiments/qubit_characterizations.py index bb9900724f6..eca59f1877a 100644 --- a/cirq-core/cirq/experiments/qubit_characterizations.py +++ b/cirq-core/cirq/experiments/qubit_characterizations.py @@ -434,7 +434,7 @@ def parallel_single_qubit_randomized_benchmarking( repetitions: The number of repetitions of each circuit. xy_only: whether to use only gates that have either an X or a Y component. if True, this excludes I, Z, Z/2, -Z/2. - xy_only: whether to use only gates that have either an X or a Z component. + xz_only: whether to use only gates that have either an X or a Z component. if True, this excludes I, Y, Y/2, -Y/2. Returns: A dictionary from qubits to RandomizedBenchMarkResult objects. From 22ccdeb8dbfa420865bcd0301e9c308a01940d27 Mon Sep 17 00:00:00 2001 From: Noureldin Date: Wed, 25 Jun 2025 20:25:54 -0700 Subject: [PATCH 10/20] Update qubit_characterizations.py --- cirq-core/cirq/experiments/qubit_characterizations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cirq-core/cirq/experiments/qubit_characterizations.py b/cirq-core/cirq/experiments/qubit_characterizations.py index eca59f1877a..863864a3820 100644 --- a/cirq-core/cirq/experiments/qubit_characterizations.py +++ b/cirq-core/cirq/experiments/qubit_characterizations.py @@ -414,7 +414,7 @@ def parallel_single_qubit_randomized_benchmarking( ), num_circuits: int = 10, repetitions: int = 1000, - xy_only: bool = False, + xy_only: bool = True, xz_only: bool = False, ) -> ParallelRandomizedBenchmarkingResult: """Clifford-based randomized benchmarking (RB) single qubits in parallel. From 6bb8c90020b441d35edee119bd1ba56fac2e8b44 Mon Sep 17 00:00:00 2001 From: Noureldin Date: Thu, 26 Jun 2025 06:53:32 -0700 Subject: [PATCH 11/20] Update cirq-core/cirq/experiments/qubit_characterizations.py Co-authored-by: Pavol Juhas --- cirq-core/cirq/experiments/qubit_characterizations.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cirq-core/cirq/experiments/qubit_characterizations.py b/cirq-core/cirq/experiments/qubit_characterizations.py index 863864a3820..237b0abbf44 100644 --- a/cirq-core/cirq/experiments/qubit_characterizations.py +++ b/cirq-core/cirq/experiments/qubit_characterizations.py @@ -398,9 +398,8 @@ def _gateset_selector( ) -> list[list[ops.SingleQubitCliffordGate]]: clifford_group = _single_qubit_cliffords() sequences = clifford_group.c1_in_xy if use_xy_basis else clifford_group.c1_in_xz - filter_seq = lambda seq: len(seq) == 2 if xy_only or xz_only: - sequences = list(filter(filter_seq, sequences)) + sequences = [seq for seq in sequences if len(seq) == 2] return _canonize_clifford_sequences(sequences) From 256c43e6683cea4242faaa487002c8d45af34065 Mon Sep 17 00:00:00 2001 From: Nour Yosri Date: Mon, 30 Jun 2025 11:38:49 -0700 Subject: [PATCH 12/20] checkpoint --- .../experiments/qubit_characterizations.py | 163 ++++++++++++++---- .../qubit_characterizations_test.py | 19 +- 2 files changed, 139 insertions(+), 43 deletions(-) diff --git a/cirq-core/cirq/experiments/qubit_characterizations.py b/cirq-core/cirq/experiments/qubit_characterizations.py index 237b0abbf44..c66224cc6ec 100644 --- a/cirq-core/cirq/experiments/qubit_characterizations.py +++ b/cirq-core/cirq/experiments/qubit_characterizations.py @@ -17,8 +17,10 @@ import dataclasses import functools import itertools -from typing import Any, cast, Iterator, Mapping, Sequence, TYPE_CHECKING +import numbers +from typing import Any, cast, Iterator, Mapping, overload, Sequence, TYPE_CHECKING +import attrs import numpy as np from matplotlib import pyplot as plt @@ -28,6 +30,7 @@ import cirq.vis.heatmap as cirq_heatmap import cirq.vis.histogram as cirq_histogram from cirq import circuits, ops, protocols +from cirq._compat import deprecated from cirq.devices import grid_qubit if TYPE_CHECKING: @@ -339,6 +342,38 @@ def plot( return axes +@attrs.frozen +class RBParameters: + r"""Parameters for running randomized benchmarking. + + Arguments: + 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. + use_xy_basis: Determines if the Clifford gates are built with x and y + rotations (True) or x and z rotations (False). + strict_basis: whether to use only cliffords that can be represented by at + most 2 gates of the choses basis. For example, + if True and use_xy_basis is True, this excludes $I, Z, \sqrt(Z), \-sqrt(Z)^\dagger$. + if True and use_xy_basis is False, this excludes $I, Y, \sqrt(Y), -\sqrt(Y)^\dagger$. + """ + + num_clifford_range: Sequence[int] = tuple(np.logspace(np.log10(5), 3, 5, dtype=int)) + num_circuits: int = 10 + repetitions: int = 600 + use_xy_basis: bool = True + strict_basis: bool = True + + def gateset(self) -> list[list[ops.SingleQubitCliffordGate]]: + clifford_group = _single_qubit_cliffords() + sequences = clifford_group.c1_in_xy if self.use_xy_basis else clifford_group.c1_in_xz + if self.strict_basis: + sequences = [seq for seq in sequences if len(seq) == 2] + return _canonize_clifford_sequences(sequences) + + +@deprecated(deadline='v2.0', fix='please use single_qubit_rb instead') def single_qubit_randomized_benchmarking( sampler: cirq.Sampler, qubit: cirq.Qid, @@ -382,27 +417,20 @@ def single_qubit_randomized_benchmarking( A RandomizedBenchMarkResult object that stores and plots the result. """ - result = parallel_single_qubit_randomized_benchmarking( + return parallel_single_qubit_rb( sampler, - (qubit,), - use_xy_basis, - num_clifford_range=num_clifford_range, - num_circuits=num_circuits, - repetitions=repetitions, + qubit, + RBParameters( + num_clifford_range=num_clifford_range, + num_circuits=num_circuits, + repetitions=repetitions, + use_xy_basis=use_xy_basis, + strict_basis=False, + ), ) - return result.results_dictionary[qubit] - - -def _gateset_selector( - use_xy_basis: bool, xy_only: bool, xz_only: bool -) -> list[list[ops.SingleQubitCliffordGate]]: - clifford_group = _single_qubit_cliffords() - sequences = clifford_group.c1_in_xy if use_xy_basis else clifford_group.c1_in_xz - if xy_only or xz_only: - sequences = [seq for seq in sequences if len(seq) == 2] - return _canonize_clifford_sequences(sequences) +@deprecated(deadline='v2.0', fix='please use single_qubit_rb instead') def parallel_single_qubit_randomized_benchmarking( sampler: cirq.Sampler, qubits: Sequence[cirq.Qid], @@ -413,8 +441,6 @@ def parallel_single_qubit_randomized_benchmarking( ), num_circuits: int = 10, repetitions: int = 1000, - xy_only: bool = True, - xz_only: bool = False, ) -> ParallelRandomizedBenchmarkingResult: """Clifford-based randomized benchmarking (RB) single qubits in parallel. @@ -431,37 +457,93 @@ def parallel_single_qubit_randomized_benchmarking( num_circuits: The number of random circuits generated for each number of Cliffords. repetitions: The number of repetitions of each circuit. - xy_only: whether to use only gates that have either an X or a Y component. - if True, this excludes I, Z, Z/2, -Z/2. - xz_only: whether to use only gates that have either an X or a Z component. - if True, this excludes I, Y, Y/2, -Y/2. Returns: A dictionary from qubits to RandomizedBenchMarkResult objects. """ + return parallel_single_qubit_rb( + sampler, + qubits, + RBParameters( + num_clifford_range=num_clifford_range, + num_circuits=num_circuits, + repetitions=repetitions, + use_xy_basis=use_xy_basis, + strict_basis=False, + ), + ) + - c1 = _gateset_selector(use_xy_basis, xy_only, xz_only) +@overload +def parallel_single_qubit_rb( + sampler: cirq.Sampler, + qubits: cirq.Qid, + parameters: RBParameters = RBParameters(), + rng_or_seed: np.random.Generator | numbers.Integeral | None = None, +) -> RandomizedBenchMarkResult: ... + + +@overload +def parallel_single_qubit_rb( + sampler: cirq.Sampler, + qubits: Sequence[cirq.Qid], + parameters: RBParameters = RBParameters(), + rng_or_seed: np.random.Generator | numbers.Integeral | None = None, +) -> ParallelRandomizedBenchmarkingResult: ... + + +def parallel_single_qubit_rb( + sampler: cirq.Sampler, + qubits: Sequence[cirq.Qid] | cirq.Qid, + parameters: RBParameters = RBParameters(), + rng_or_seed: np.random.Generator | numbers.Integeral | None = None, +) -> RandomizedBenchMarkResult | ParallelRandomizedBenchmarkingResult: + """Clifford-based randomized benchmarking (RB) single qubits in parallel. + + Args: + sampler: The quantum engine or simulator to run the circuits. + qubits: The qubit(s) to benchmark. + parameters: The parameters of the experiment. + rng_or_seed: A np.random.Generator object or seed. + Returns: + A dictionary from qubits to RandomizedBenchMarkResult objects. + """ + is_single_qubit = False + if isinstance(qubits, ops.Qid): + qubits = (qubits,) + is_single_qubit = True + + rng_or_seed = ( + rng_or_seed + if isinstance(rng_or_seed, np.random.Generator) + else np.random.default_rng(rng_or_seed) + ) + + c1 = parameters.gateset() # create circuits circuits_all: list[cirq.AbstractCircuit] = [] - for num_cliffords in num_clifford_range: - for _ in range(num_circuits): - circuits_all.append(_create_parallel_rb_circuit(qubits, num_cliffords, c1)) + for num_cliffords in parameters.num_clifford_range: + for _ in range(parameters.num_circuits): + circuits_all.append(_create_parallel_rb_circuit(qubits, num_cliffords, c1, rng_or_seed)) # run circuits - results = sampler.run_batch(circuits_all, repetitions=repetitions) + results = sampler.run_batch(circuits_all, repetitions=parameters.repetitions) gnd_probs: dict = {q: [] for q in qubits} idx = 0 - for num_cliffords in num_clifford_range: + for num_cliffords in parameters.num_clifford_range: excited_probs: dict[cirq.Qid, list[float]] = {q: [] for q in qubits} - for _ in range(num_circuits): + for _ in range(parameters.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])) + + if is_single_qubit: + return RandomizedBenchMarkResult(parameters.num_clifford_range, gnd_probs[qubits[0]]) return ParallelRandomizedBenchmarkingResult( - {q: RandomizedBenchMarkResult(num_clifford_range, gnd_probs[q]) for q in qubits} + {q: RandomizedBenchMarkResult(parameters.num_clifford_range, gnd_probs[q]) for q in qubits} ) @@ -697,9 +779,14 @@ def _measurement(two_qubit_circuit: circuits.Circuit) -> np.ndarray: def _create_parallel_rb_circuit( - qubits: Sequence[cirq.Qid], num_cliffords: int, c1: list[list[ops.SingleQubitCliffordGate]] + qubits: Sequence[cirq.Qid], + num_cliffords: int, + c1: list[list[ops.SingleQubitCliffordGate]], + rng: np.random.Generator | None = None, ) -> cirq.Circuit: - sequences_to_zip = [_random_single_q_clifford(qubit, num_cliffords, c1) for qubit in qubits] + sequences_to_zip = [ + _random_single_q_clifford(qubit, num_cliffords, c1, rng) for qubit in qubits + ] # Ensure each sequence has the same number of moments. num_moments = max(len(sequence) for sequence in sequences_to_zip) for q, sequence in zip(qubits, sequences_to_zip): @@ -750,10 +837,14 @@ def _two_qubit_clifford_matrices(q_0: cirq.Qid, q_1: cirq.Qid, cliffords: Cliffo def _random_single_q_clifford( - qubit: cirq.Qid, num_cfds: int, cfds: Sequence[Sequence[cirq.ops.SingleQubitCliffordGate]] + qubit: cirq.Qid, + num_cfds: int, + cfds: Sequence[Sequence[cirq.ops.SingleQubitCliffordGate]], + rng: np.random.Generator | None = None, ) -> list[cirq.Operation]: operations = [[gate.to_phased_xz_gate()(qubit) for gate in gates] for gates in cfds] - gate_ids = np.random.choice(len(cfds), num_cfds).tolist() + choice_fn = rng.choice if rng else np.random.choice + gate_ids = choice_fn(len(cfds), num_cfds).tolist() adjoint = _reduce_gate_seq([gate for gate_id in gate_ids for gate in cfds[gate_id]]) ** -1 return [op for gate_id in gate_ids for op in operations[gate_id]] + [ adjoint.to_phased_xz_gate()(qubit) diff --git a/cirq-core/cirq/experiments/qubit_characterizations_test.py b/cirq-core/cirq/experiments/qubit_characterizations_test.py index 05a548a588d..a858e63aed9 100644 --- a/cirq-core/cirq/experiments/qubit_characterizations_test.py +++ b/cirq-core/cirq/experiments/qubit_characterizations_test.py @@ -14,6 +14,9 @@ from __future__ import annotations +import os +from unittest import mock + import matplotlib.pyplot as plt import numpy as np import pytest @@ -23,6 +26,7 @@ from cirq import circuits, GridQubit, ops, sim from cirq.experiments import ( parallel_single_qubit_randomized_benchmarking, + parallel_single_qubit_rb, single_qubit_randomized_benchmarking, single_qubit_state_tomography, two_qubit_randomized_benchmarking, @@ -104,6 +108,7 @@ def check_distinct(unitaries): assert num_x <= 1 +@mock.patch.dict(os.environ, clear='CIRQ_TESTING') def test_single_qubit_randomized_benchmarking(): # Check that the ground state population at the end of the Clifford # sequences is always unity. @@ -116,7 +121,8 @@ 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(): +@mock.patch.dict(os.environ, clear='CIRQ_TESTING') +def test_parallel_single_qubit_parallel_single_qubit_randomized_benchmarking(): # Check that the ground state population at the end of the Clifford # sequences is always unity. simulator = sim.Simulator() @@ -231,13 +237,12 @@ def test_tomography_plot_raises_for_incorrect_number_of_axes(): @pytest.mark.parametrize('num_cliffords', range(5, 10)) @pytest.mark.parametrize('use_xy_basis', [False, True]) -@pytest.mark.parametrize('xy_only', [False, True]) -@pytest.mark.parametrize('xz_only', [False, True]) -def test_single_qubit_cliffords_gateset(num_cliffords, use_xy_basis, xy_only, xz_only): +@pytest.mark.parametrize('strict_basis', [False, True]) +def test_single_qubit_cliffords_gateset(num_cliffords, use_xy_basis, strict_basis): qubits = [GridQubit(0, i) for i in range(4)] - c1_in_xy = cirq.experiments.qubit_characterizations._gateset_selector( - use_xy_basis, xy_only, xz_only - ) + c1_in_xy = cirq.experiments.qubit_characterizations.RBParameters( + use_xy_basis=use_xy_basis, strict_basis=strict_basis + ).gateset() c = cirq.experiments.qubit_characterizations._create_parallel_rb_circuit( qubits, num_cliffords, c1_in_xy ) From b552d21201f058632bda409bbd9ede5902975193 Mon Sep 17 00:00:00 2001 From: Nour Yosri Date: Mon, 30 Jun 2025 11:50:22 -0700 Subject: [PATCH 13/20] nits --- cirq-core/cirq/experiments/qubit_characterizations.py | 11 +++++------ .../cirq/experiments/qubit_characterizations_test.py | 1 - 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/cirq-core/cirq/experiments/qubit_characterizations.py b/cirq-core/cirq/experiments/qubit_characterizations.py index c66224cc6ec..7bf01608496 100644 --- a/cirq-core/cirq/experiments/qubit_characterizations.py +++ b/cirq-core/cirq/experiments/qubit_characterizations.py @@ -17,7 +17,6 @@ import dataclasses import functools import itertools -import numbers from typing import Any, cast, Iterator, Mapping, overload, Sequence, TYPE_CHECKING import attrs @@ -362,7 +361,7 @@ class RBParameters: num_clifford_range: Sequence[int] = tuple(np.logspace(np.log10(5), 3, 5, dtype=int)) num_circuits: int = 10 repetitions: int = 600 - use_xy_basis: bool = True + use_xy_basis: bool = False strict_basis: bool = True def gateset(self) -> list[list[ops.SingleQubitCliffordGate]]: @@ -377,7 +376,7 @@ def gateset(self) -> list[list[ops.SingleQubitCliffordGate]]: def single_qubit_randomized_benchmarking( sampler: cirq.Sampler, qubit: cirq.Qid, - use_xy_basis: bool = False, + use_xy_basis: bool = True, *, num_clifford_range: Sequence[int] = tuple(np.logspace(np.log10(5), 3, 5, dtype=int)), num_circuits: int = 10, @@ -434,7 +433,7 @@ def single_qubit_randomized_benchmarking( def parallel_single_qubit_randomized_benchmarking( sampler: cirq.Sampler, qubits: Sequence[cirq.Qid], - use_xy_basis: bool = False, + use_xy_basis: bool = True, *, num_clifford_range: Sequence[int] = tuple( np.logspace(np.log10(5), np.log10(1000), 5, dtype=int) @@ -478,7 +477,7 @@ def parallel_single_qubit_rb( sampler: cirq.Sampler, qubits: cirq.Qid, parameters: RBParameters = RBParameters(), - rng_or_seed: np.random.Generator | numbers.Integeral | None = None, + rng_or_seed: np.random.Generator | int | None = None, ) -> RandomizedBenchMarkResult: ... @@ -487,7 +486,7 @@ def parallel_single_qubit_rb( sampler: cirq.Sampler, qubits: Sequence[cirq.Qid], parameters: RBParameters = RBParameters(), - rng_or_seed: np.random.Generator | numbers.Integeral | None = None, + rng_or_seed: np.random.Generator | int | None = None, ) -> ParallelRandomizedBenchmarkingResult: ... diff --git a/cirq-core/cirq/experiments/qubit_characterizations_test.py b/cirq-core/cirq/experiments/qubit_characterizations_test.py index a858e63aed9..5c38ac22d4a 100644 --- a/cirq-core/cirq/experiments/qubit_characterizations_test.py +++ b/cirq-core/cirq/experiments/qubit_characterizations_test.py @@ -26,7 +26,6 @@ from cirq import circuits, GridQubit, ops, sim from cirq.experiments import ( parallel_single_qubit_randomized_benchmarking, - parallel_single_qubit_rb, single_qubit_randomized_benchmarking, single_qubit_state_tomography, two_qubit_randomized_benchmarking, From f0ebcd2b26314146192b4f0fceec6817277be03b Mon Sep 17 00:00:00 2001 From: Nour Yosri Date: Mon, 30 Jun 2025 11:52:34 -0700 Subject: [PATCH 14/20] lint --- cirq-core/cirq/experiments/qubit_characterizations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cirq-core/cirq/experiments/qubit_characterizations.py b/cirq-core/cirq/experiments/qubit_characterizations.py index 7bf01608496..d6e259b8c1f 100644 --- a/cirq-core/cirq/experiments/qubit_characterizations.py +++ b/cirq-core/cirq/experiments/qubit_characterizations.py @@ -494,7 +494,7 @@ def parallel_single_qubit_rb( sampler: cirq.Sampler, qubits: Sequence[cirq.Qid] | cirq.Qid, parameters: RBParameters = RBParameters(), - rng_or_seed: np.random.Generator | numbers.Integeral | None = None, + rng_or_seed: np.random.Generator | int | None = None, ) -> RandomizedBenchMarkResult | ParallelRandomizedBenchmarkingResult: """Clifford-based randomized benchmarking (RB) single qubits in parallel. From cf27bb9d03fd65d10a7fe0c51f3f59e6501d767a Mon Sep 17 00:00:00 2001 From: Nour Yosri Date: Mon, 7 Jul 2025 16:42:34 -0700 Subject: [PATCH 15/20] relax the bounds in curve fitting and change the way we exclude gates in strict gatesets --- .../cirq/experiments/qubit_characterizations.py | 12 +++++++++--- .../cirq/experiments/qubit_characterizations_test.py | 4 ++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/cirq-core/cirq/experiments/qubit_characterizations.py b/cirq-core/cirq/experiments/qubit_characterizations.py index d6e259b8c1f..9624641cc08 100644 --- a/cirq-core/cirq/experiments/qubit_characterizations.py +++ b/cirq-core/cirq/experiments/qubit_characterizations.py @@ -142,7 +142,7 @@ def _fit_exponential(self) -> tuple[np.ndarray, np.ndarray]: xdata=self._num_cfds_seq, ydata=self._gnd_state_probs, p0=[0.5, 0.5, 1.0 - 1e-3], - bounds=([0, 0.25, 0], [0.5, 0.75, 1]), + bounds=([-1, -1, 0], [1, 1, 1]), ) @@ -367,9 +367,15 @@ class RBParameters: def gateset(self) -> list[list[ops.SingleQubitCliffordGate]]: clifford_group = _single_qubit_cliffords() sequences = clifford_group.c1_in_xy if self.use_xy_basis else clifford_group.c1_in_xz + sequences = _canonize_clifford_sequences(sequences) if self.strict_basis: - sequences = [seq for seq in sequences if len(seq) == 2] - return _canonize_clifford_sequences(sequences) + if self.use_xy_basis: + excluded_gates = ops.Gateset(ops.I, ops.Z, ops.Z**0.5, ops.Z**-0.5) + else: + excluded_gates = ops.Gateset(ops.I, ops.Y, ops.Y**0.5, ops.Y**-0.5) + + sequences = [[g] for (g,) in sequences if g not in excluded_gates] + return sequences @deprecated(deadline='v2.0', fix='please use single_qubit_rb instead') diff --git a/cirq-core/cirq/experiments/qubit_characterizations_test.py b/cirq-core/cirq/experiments/qubit_characterizations_test.py index 5c38ac22d4a..9f4c9e6569e 100644 --- a/cirq-core/cirq/experiments/qubit_characterizations_test.py +++ b/cirq-core/cirq/experiments/qubit_characterizations_test.py @@ -242,6 +242,10 @@ def test_single_qubit_cliffords_gateset(num_cliffords, use_xy_basis, strict_basi c1_in_xy = cirq.experiments.qubit_characterizations.RBParameters( use_xy_basis=use_xy_basis, strict_basis=strict_basis ).gateset() + if strict_basis: + assert len(c1_in_xy) == 20 + else: + assert len(c1_in_xy) == 24 c = cirq.experiments.qubit_characterizations._create_parallel_rb_circuit( qubits, num_cliffords, c1_in_xy ) From b3ce46946804ebe8ef79a544f6e5e836bdbeec19 Mon Sep 17 00:00:00 2001 From: Nour Yosri Date: Mon, 7 Jul 2025 16:51:49 -0700 Subject: [PATCH 16/20] address comments about deprecation --- .../experiments/qubit_characterizations.py | 43 +++++++++---------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/cirq-core/cirq/experiments/qubit_characterizations.py b/cirq-core/cirq/experiments/qubit_characterizations.py index 9624641cc08..cda683c316c 100644 --- a/cirq-core/cirq/experiments/qubit_characterizations.py +++ b/cirq-core/cirq/experiments/qubit_characterizations.py @@ -17,7 +17,7 @@ import dataclasses import functools import itertools -from typing import Any, cast, Iterator, Mapping, overload, Sequence, TYPE_CHECKING +from typing import Any, cast, Iterator, Mapping, Sequence, TYPE_CHECKING import attrs import numpy as np @@ -422,7 +422,7 @@ def single_qubit_randomized_benchmarking( A RandomizedBenchMarkResult object that stores and plots the result. """ - return parallel_single_qubit_rb( + return single_qubit_rb( sampler, qubit, RBParameters( @@ -435,7 +435,7 @@ def single_qubit_randomized_benchmarking( ) -@deprecated(deadline='v2.0', fix='please use single_qubit_rb instead') +@deprecated(deadline='v2.0', fix='please use parallel_single_qubit_rb instead') def parallel_single_qubit_randomized_benchmarking( sampler: cirq.Sampler, qubits: Sequence[cirq.Qid], @@ -478,30 +478,33 @@ def parallel_single_qubit_randomized_benchmarking( ) -@overload -def parallel_single_qubit_rb( +def single_qubit_rb( sampler: cirq.Sampler, - qubits: cirq.Qid, + qubit: cirq.Qid, parameters: RBParameters = RBParameters(), rng_or_seed: np.random.Generator | int | None = None, -) -> RandomizedBenchMarkResult: ... - +) -> RandomizedBenchMarkResult: + """Clifford-based randomized benchmarking (RB) on a single qubit. -@overload -def parallel_single_qubit_rb( - sampler: cirq.Sampler, - qubits: Sequence[cirq.Qid], - parameters: RBParameters = RBParameters(), - rng_or_seed: np.random.Generator | int | None = None, -) -> ParallelRandomizedBenchmarkingResult: ... + Args: + sampler: The quantum engine or simulator to run the circuits. + qubits: The qubit(s) to benchmark. + parameters: The parameters of the experiment. + rng_or_seed: A np.random.Generator object or seed. + Returns: + A dictionary from qubits to RandomizedBenchMarkResult objects. + """ + return parallel_single_qubit_rb(sampler, [qubit], parameters, rng_or_seed).results_dictionary[ + qubit + ] def parallel_single_qubit_rb( sampler: cirq.Sampler, - qubits: Sequence[cirq.Qid] | cirq.Qid, + qubits: Sequence[cirq.Qid], parameters: RBParameters = RBParameters(), rng_or_seed: np.random.Generator | int | None = None, -) -> RandomizedBenchMarkResult | ParallelRandomizedBenchmarkingResult: +) -> ParallelRandomizedBenchmarkingResult: """Clifford-based randomized benchmarking (RB) single qubits in parallel. Args: @@ -512,10 +515,6 @@ def parallel_single_qubit_rb( Returns: A dictionary from qubits to RandomizedBenchMarkResult objects. """ - is_single_qubit = False - if isinstance(qubits, ops.Qid): - qubits = (qubits,) - is_single_qubit = True rng_or_seed = ( rng_or_seed @@ -545,8 +544,6 @@ def parallel_single_qubit_rb( for qubit in qubits: gnd_probs[qubit].append(1.0 - np.mean(excited_probs[qubit])) - if is_single_qubit: - return RandomizedBenchMarkResult(parameters.num_clifford_range, gnd_probs[qubits[0]]) return ParallelRandomizedBenchmarkingResult( {q: RandomizedBenchMarkResult(parameters.num_clifford_range, gnd_probs[q]) for q in qubits} ) From 16911ff276e00976ca90d8e80f2b64e7d18f62e1 Mon Sep 17 00:00:00 2001 From: Nour Yosri Date: Mon, 7 Jul 2025 16:59:07 -0700 Subject: [PATCH 17/20] fix failing test --- cirq-core/cirq/experiments/__init__.py | 3 +++ cirq-core/cirq/experiments/two_qubit_xeb.py | 13 ++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/cirq-core/cirq/experiments/__init__.py b/cirq-core/cirq/experiments/__init__.py index 21df19acda1..8df1351d500 100644 --- a/cirq-core/cirq/experiments/__init__.py +++ b/cirq-core/cirq/experiments/__init__.py @@ -15,12 +15,15 @@ from cirq.experiments.qubit_characterizations import ( RandomizedBenchMarkResult as RandomizedBenchMarkResult, + RBParameters as RBParameters, single_qubit_randomized_benchmarking as single_qubit_randomized_benchmarking, + single_qubit_rb as single_qubit_rb, single_qubit_state_tomography as single_qubit_state_tomography, TomographyResult as TomographyResult, two_qubit_randomized_benchmarking as two_qubit_randomized_benchmarking, two_qubit_state_tomography as two_qubit_state_tomography, parallel_single_qubit_randomized_benchmarking as parallel_single_qubit_randomized_benchmarking, + parallel_single_qubit_rb as parallel_single_qubit_rb, ) from cirq.experiments.fidelity_estimation import ( diff --git a/cirq-core/cirq/experiments/two_qubit_xeb.py b/cirq-core/cirq/experiments/two_qubit_xeb.py index fdd85a6647e..149edcfcd64 100644 --- a/cirq-core/cirq/experiments/two_qubit_xeb.py +++ b/cirq-core/cirq/experiments/two_qubit_xeb.py @@ -30,8 +30,9 @@ from cirq._compat import cached_method from cirq.experiments import random_quantum_circuit_generation as rqcg from cirq.experiments.qubit_characterizations import ( - parallel_single_qubit_randomized_benchmarking, + parallel_single_qubit_rb, ParallelRandomizedBenchmarkingResult, + RBParameters, ) from cirq.experiments.xeb_fitting import ( benchmark_2q_xeb_fidelities, @@ -586,12 +587,14 @@ def run_rb_and_xeb( qubits, pairs = qubits_and_pairs(sampler, qubits, pairs) - rb = parallel_single_qubit_randomized_benchmarking( + rb = parallel_single_qubit_rb( sampler=sampler, qubits=qubits, - repetitions=repetitions, - num_circuits=num_circuits, - num_clifford_range=num_clifford_range, + parameters=RBParameters( + num_circuits=num_circuits, + repetitions=repetitions, + num_clifford_range=num_clifford_range, + ), ) xeb = parallel_two_qubit_xeb( From 047d1a84cfa628d09fe2637fbe43f6cf828ef701 Mon Sep 17 00:00:00 2001 From: Nour Yosri Date: Mon, 7 Jul 2025 17:02:24 -0700 Subject: [PATCH 18/20] lint --- cirq-core/cirq/experiments/qubit_characterizations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cirq-core/cirq/experiments/qubit_characterizations.py b/cirq-core/cirq/experiments/qubit_characterizations.py index cda683c316c..1445842ff24 100644 --- a/cirq-core/cirq/experiments/qubit_characterizations.py +++ b/cirq-core/cirq/experiments/qubit_characterizations.py @@ -488,7 +488,7 @@ def single_qubit_rb( Args: sampler: The quantum engine or simulator to run the circuits. - qubits: The qubit(s) to benchmark. + qubit: The qubit(s) to benchmark. parameters: The parameters of the experiment. rng_or_seed: A np.random.Generator object or seed. Returns: From 91e2c1e186ea0fae43b405a0a8c48cc313fa74f9 Mon Sep 17 00:00:00 2001 From: Nour Yosri Date: Mon, 7 Jul 2025 17:09:08 -0700 Subject: [PATCH 19/20] nit --- cirq-core/cirq/experiments/qubit_characterizations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cirq-core/cirq/experiments/qubit_characterizations.py b/cirq-core/cirq/experiments/qubit_characterizations.py index 1445842ff24..6da94586dfb 100644 --- a/cirq-core/cirq/experiments/qubit_characterizations.py +++ b/cirq-core/cirq/experiments/qubit_characterizations.py @@ -142,7 +142,7 @@ def _fit_exponential(self) -> tuple[np.ndarray, np.ndarray]: xdata=self._num_cfds_seq, ydata=self._gnd_state_probs, p0=[0.5, 0.5, 1.0 - 1e-3], - bounds=([-1, -1, 0], [1, 1, 1]), + bounds=([0, -1, 0], [1, 1, 1]), ) From 722e90789e3470d6c3f0cebc12e13e6ecddac711 Mon Sep 17 00:00:00 2001 From: Nour Yosri Date: Mon, 7 Jul 2025 17:26:35 -0700 Subject: [PATCH 20/20] fix failing test --- examples/qubit_characterizations_example.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/qubit_characterizations_example.py b/examples/qubit_characterizations_example.py index 6153a0c63dd..38e265e6217 100644 --- a/examples/qubit_characterizations_example.py +++ b/examples/qubit_characterizations_example.py @@ -34,8 +34,10 @@ def main(minimum_cliffords=5, maximum_cliffords=20, cliffords_step=5): clifford_range = range(minimum_cliffords, maximum_cliffords, cliffords_step) # Clifford-based randomized benchmarking of single-qubit gates on q_0. - rb_result_1q = cirq.experiments.single_qubit_randomized_benchmarking( - simulator, q_0, num_clifford_range=clifford_range, repetitions=100 + rb_result_1q = cirq.experiments.single_qubit_rb( + simulator, + q_0, + cirq.experiments.RBParameters(num_clifford_range=clifford_range, repetitions=100), ) rb_result_1q.plot()