From a19846a553283d02fa0769bec760d76935a75041 Mon Sep 17 00:00:00 2001 From: Nour Yosri Date: Tue, 2 Jan 2024 16:05:56 -0800 Subject: [PATCH 01/12] make SingleQubitCliffordGate immutable singletons and use it in benchmarking --- .../experiments/qubit_characterizations.py | 71 +++++++++++-------- .../qubit_characterizations_test.py | 8 +-- cirq-core/cirq/ops/clifford_gate.py | 39 +++++++++- cirq-core/cirq/qis/clifford_tableau.py | 3 + 4 files changed, 83 insertions(+), 38 deletions(-) diff --git a/cirq-core/cirq/experiments/qubit_characterizations.py b/cirq-core/cirq/experiments/qubit_characterizations.py index a5c3e7922a8..898a887d479 100644 --- a/cirq-core/cirq/experiments/qubit_characterizations.py +++ b/cirq-core/cirq/experiments/qubit_characterizations.py @@ -58,11 +58,11 @@ class Cliffords: s1_y """ - c1_in_xy: List[List[ops.Gate]] - c1_in_xz: List[List[ops.Gate]] - s1: List[List[ops.Gate]] - s1_x: List[List[ops.Gate]] - s1_y: List[List[ops.Gate]] + 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: @@ -299,17 +299,14 @@ def parallel_single_qubit_randomized_benchmarking( 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]) + clifford_group = _single_qubit_cliffords() + c1 = clifford_group.c1_in_xy if use_xy_basis else clifford_group.c1_in_xz # 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, clifford_mats) - ) + circuits_all.append(_create_parallel_rb_circuit(qubits, num_cliffords, c1)) # run circuits results = sampler.run_batch(circuits_all, repetitions=repetitions) @@ -562,11 +559,9 @@ def _measurement(two_qubit_circuit: circuits.Circuit) -> np.ndarray: def _create_parallel_rb_circuit( - qubits: Iterator['cirq.Qid'], num_cliffords: int, c1: list, clifford_mats: np.ndarray + qubits: Iterator['cirq.Qid'], num_cliffords: int, c1: list ) -> 'cirq.Circuit': - circuits_to_zip = [ - _random_single_q_clifford(qubit, num_cliffords, c1, clifford_mats) for qubit in qubits - ] + circuits_to_zip = [_random_single_q_clifford(qubit, num_cliffords, c1) for qubit in qubits] circuit = circuits.Circuit.zip(*circuits_to_zip) return circuits.Circuit.from_moments(*circuit, ops.measure_each(*qubits)) @@ -612,16 +607,12 @@ def _two_qubit_clifford_matrices( def _random_single_q_clifford( - qubit: 'cirq.Qid', - num_cfds: int, - cfds: Sequence[Sequence['cirq.Gate']], - cfd_matrices: np.ndarray, + qubit: 'cirq.Qid', num_cfds: int, cfds: Sequence[Sequence['cirq.Gate']] ) -> 'cirq.Circuit': clifford_group_size = 24 gate_ids = list(np.random.choice(clifford_group_size, num_cfds)) gate_sequence = [gate for gate_id in gate_ids for gate in cfds[gate_id]] - idx = _find_inv_matrix(_gate_seq_to_mats(gate_sequence), cfd_matrices) - gate_sequence.extend(cfds[idx]) + gate_sequence.append(_reduce_gate_seq(gate_sequence) ** -1) circuit = circuits.Circuit(gate(qubit) for gate in gate_sequence) return circuit @@ -681,11 +672,13 @@ def _matrix_bar_plot( ax.set_title(title) -def _gate_seq_to_mats(gate_seq: Sequence['cirq.Gate']) -> np.ndarray: - mat_rep = protocols.unitary(gate_seq[0]) +def _reduce_gate_seq( + gate_seq: Sequence[ops.SingleQubitCliffordGate], +) -> ops.SingleQubitCliffordGate: + cur = gate_seq[0] for gate in gate_seq[1:]: - mat_rep = np.dot(protocols.unitary(gate), mat_rep) - return mat_rep + cur = cur.merged_with(gate) + return cur def _two_qubit_clifford( @@ -794,10 +787,14 @@ def _single_qubit_gates( def _single_qubit_cliffords() -> Cliffords: - X, Y, Z = ops.X, ops.Y, ops.Z + X, Y, Z = ( + ops.SingleQubitCliffordGate.X, + ops.SingleQubitCliffordGate.Y, + ops.SingleQubitCliffordGate.Z, + ) - c1_in_xy: List[List['cirq.Gate']] = [] - c1_in_xz: List[List['cirq.Gate']] = [] + c1_in_xy: List[List[ops.SingleQubitCliffordGate]] = [] + c1_in_xz: List[List[ops.SingleQubitCliffordGate]] = [] for phi_0, phi_1 in itertools.product([1.0, 0.5, -0.5], [0.0, 0.5, -0.5]): c1_in_xy.append([X**phi_0, Y**phi_1]) @@ -820,8 +817,20 @@ def _single_qubit_cliffords() -> Cliffords: for z0, x, z1 in phi_xz: c1_in_xz.append([Z**z0, X**x, Z**z1]) - s1: List[List['cirq.Gate']] = [[X**0.0], [Y**0.5, X**0.5], [X**-0.5, Y**-0.5]] - s1_x: List[List['cirq.Gate']] = [[X**0.5], [X**0.5, Y**0.5, X**0.5], [Y**-0.5]] - s1_y: List[List['cirq.Gate']] = [[Y**0.5], [X**-0.5, Y**-0.5, X**0.5], [Y, X**0.5]] + s1: List[List[ops.SingleQubitCliffordGate]] = [ + [X**0.0], + [Y**0.5, X**0.5], + [X**-0.5, Y**-0.5], + ] + s1_x: List[List[ops.SingleQubitCliffordGate]] = [ + [X**0.5], + [X**0.5, Y**0.5, X**0.5], + [Y**-0.5], + ] + s1_y: List[List[ops.SingleQubitCliffordGate]] = [ + [Y**0.5], + [X**-0.5, Y**-0.5, X**0.5], + [Y, X**0.5], + ] return Cliffords(c1_in_xy, c1_in_xz, s1, s1_x, s1_y) diff --git a/cirq-core/cirq/experiments/qubit_characterizations_test.py b/cirq-core/cirq/experiments/qubit_characterizations_test.py index ba205ad592d..733a68e0b0a 100644 --- a/cirq-core/cirq/experiments/qubit_characterizations_test.py +++ b/cirq-core/cirq/experiments/qubit_characterizations_test.py @@ -73,12 +73,12 @@ def check_distinct(unitaries): for p in PAULIS: assert is_pauli(u @ p @ u.conj().T), str(u) - # Check that XZ decomposition has at most one X gate per clifford. + # Check that XZ decomposition has at most one X gate and one Z gate per clifford. for gates in cliffords.c1_in_xz: - num_x = len([gate for gate in gates if isinstance(gate, cirq.XPowGate)]) - num_z = len([gate for gate in gates if isinstance(gate, cirq.ZPowGate)]) - assert num_x + num_z == len(gates) + num_x = len([gate for gate in gates if gate == cirq.ops.SingleQubitCliffordGate.X]) + num_z = len([gate for gate in gates if gate == cirq.ops.SingleQubitCliffordGate.Z]) assert num_x <= 1 + assert num_z <= 1 def test_single_qubit_randomized_benchmarking(): diff --git a/cirq-core/cirq/ops/clifford_gate.py b/cirq-core/cirq/ops/clifford_gate.py index baba0712518..e7cc7d20f9e 100644 --- a/cirq-core/cirq/ops/clifford_gate.py +++ b/cirq-core/cirq/ops/clifford_gate.py @@ -14,11 +14,13 @@ from typing import Any, Dict, List, Optional, Sequence, Tuple, TYPE_CHECKING, Union - +import functools +from dataclasses import dataclass import numpy as np from cirq import protocols, value, linalg, qis from cirq._import import LazyLoader +from cirq._compat import cached_property from cirq.ops import common_gates, named_qubit, raw_types, pauli_gates, phased_x_z_gate from cirq.ops.pauli_gates import Pauli from cirq.type_workarounds import NotImplementedType @@ -356,6 +358,8 @@ def _get_sqrt_map( class CliffordGate(raw_types.Gate, CommonCliffordGates): """Clifford rotation for N-qubit.""" + _clifford_tableau: qis.CliffordTableau + def __init__(self, *, _clifford_tableau: qis.CliffordTableau) -> None: # We use the Clifford tableau to represent a Clifford gate. # It is crucial to note that the meaning of tableau here is different @@ -376,7 +380,7 @@ def __init__(self, *, _clifford_tableau: qis.CliffordTableau) -> None: # more precisely the conjugate transformation of ZI by this gate, becomes -ZI. # (Note the real clifford tableau has to satify the Symplectic property. # here is just for illustration) - self._clifford_tableau = _clifford_tableau.copy() + object.__setattr__(self, '_clifford_tableau', _clifford_tableau.copy()) @property def clifford_tableau(self): @@ -399,6 +403,12 @@ def _has_stabilizer_effect_(self) -> Optional[bool]: def __pow__(self, exponent) -> 'CliffordGate': if exponent == -1: return CliffordGate.from_clifford_tableau(self.clifford_tableau.inverse()) + if exponent == 0: + return CliffordGate.from_clifford_tableau( + qis.CliffordTableau(num_qubits=self._num_qubits_()) + ) + if exponent == 1: + return self if exponent > 0 and int(exponent) == exponent: base_tableau = self.clifford_tableau.copy() for _ in range(int(exponent) - 1): @@ -457,6 +467,7 @@ def _act_on_( return NotImplemented +@dataclass(frozen=True, init=False, eq=False, repr=False) @value.value_equality(manual_cls=True) class SingleQubitCliffordGate(CliffordGate): """Any single qubit Clifford rotation.""" @@ -468,6 +479,7 @@ def _num_qubits_(self): return 1 @staticmethod + @functools.cache def from_clifford_tableau(tableau: qis.CliffordTableau) -> 'SingleQubitCliffordGate': if not isinstance(tableau, qis.CliffordTableau): raise ValueError('Input argument has to be a CliffordTableau instance.') @@ -679,6 +691,10 @@ def to_phased_xz_gate(self) -> phased_x_z_gate.PhasedXZGate: * {middle point of xyz in 4 Quadrant} * 120 is [[0, 1], [1, 1]] * {middle point of xyz in 4 Quadrant} * 240 is [[1, 1], [1, 0]] """ + return self._to_phased_xz_gate + + @cached_property + def _to_phased_xz_gate(self) -> phased_x_z_gate.PhasedXZGate: x_to_flip, z_to_flip = self.clifford_tableau.rs flip_index = int(z_to_flip) * 2 + x_to_flip a, x, z = 0.0, 0.0, 0.0 @@ -716,7 +732,7 @@ def to_phased_xz_gate(self) -> phased_x_z_gate.PhasedXZGate: z = -0.5 if x_to_flip else 0.5 return phased_x_z_gate.PhasedXZGate(x_exponent=x, z_exponent=z, axis_phase_exponent=a) - def __pow__(self, exponent) -> 'SingleQubitCliffordGate': + def __pow__(self, exponent: Union[float, int]) -> 'SingleQubitCliffordGate': # First to check if we can get the sqrt and negative sqrt Clifford. if self._get_sqrt_map().get(exponent, None): pow_gate = self._get_sqrt_map()[exponent].get(self, None) @@ -761,6 +777,7 @@ def commutes_with_pauli(self, pauli: Pauli) -> bool: to, flip = self.pauli_tuple(pauli) return to == pauli and not flip + @functools.cache def merged_with(self, second: 'SingleQubitCliffordGate') -> 'SingleQubitCliffordGate': """Returns a SingleQubitCliffordGate such that the circuits --output-- and --self--second-- @@ -773,6 +790,10 @@ def _has_unitary_(self) -> bool: return True def _unitary_(self) -> np.ndarray: + return self._unitary + + @cached_property + def _unitary(self) -> np.ndarray: mat = np.eye(2) qubit = named_qubit.NamedQubit('arbitrary') for op in protocols.decompose_once_with_qubits(self, (qubit,)): @@ -787,6 +808,10 @@ def decompose_gate(self) -> Sequence['cirq.Gate']: clifford gate if applied in order. This decomposition agrees with cirq.unitary(self), including global phase. """ + return self._decompose_gate + + @cached_property + def _decompose_gate(self) -> Sequence['cirq.Gate']: if self == SingleQubitCliffordGate.H: return [common_gates.H] rotations = self.decompose_rotation() @@ -802,6 +827,10 @@ def decompose_rotation(self) -> Sequence[Tuple[Pauli, int]]: Note that the combined unitary effect of these rotations may differ from cirq.unitary(self) by a global phase. """ + return self._decompose_rotation + + @cached_property + def _decompose_rotation(self) -> Sequence[Tuple[Pauli, int]]: x_rot = self.pauli_tuple(pauli_gates.X) y_rot = self.pauli_tuple(pauli_gates.Y) z_rot = self.pauli_tuple(pauli_gates.Z) @@ -895,6 +924,10 @@ def _circuit_diagram_info_( ) def _value_equality_values_(self): + return self._value_equality_values + + @cached_property + def _value_equality_values(self): return self._clifford_tableau.matrix().tobytes() + self._clifford_tableau.rs.tobytes() def _value_equality_values_cls_(self): diff --git a/cirq-core/cirq/qis/clifford_tableau.py b/cirq-core/cirq/qis/clifford_tableau.py index f7f0d334eb4..317964d1fa2 100644 --- a/cirq-core/cirq/qis/clifford_tableau.py +++ b/cirq-core/cirq/qis/clifford_tableau.py @@ -652,3 +652,6 @@ def measure( self, axes: Sequence[int], seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None ) -> List[int]: return [self._measure(axis, random_state.parse_random_state(seed)) for axis in axes] + + def __hash__(self) -> int: + return hash(self.matrix().tobytes() + self.rs.tobytes()) From 7495f3ac1d2b2fd7e2a97c6d7c677d510b695e79 Mon Sep 17 00:00:00 2001 From: Nour Yosri Date: Mon, 8 Jan 2024 11:02:49 -0800 Subject: [PATCH 02/12] address comments --- .../experiments/qubit_characterizations.py | 2 ++ .../qubit_characterizations_test.py | 31 ++++++++++++++++--- cirq-core/cirq/ops/clifford_gate.py | 4 +-- cirq-core/cirq/qis/clifford_tableau.py | 3 +- 4 files changed, 33 insertions(+), 7 deletions(-) diff --git a/cirq-core/cirq/experiments/qubit_characterizations.py b/cirq-core/cirq/experiments/qubit_characterizations.py index 898a887d479..b5f8da6e45f 100644 --- a/cirq-core/cirq/experiments/qubit_characterizations.py +++ b/cirq-core/cirq/experiments/qubit_characterizations.py @@ -14,6 +14,7 @@ import dataclasses import itertools +import functools from typing import ( Any, @@ -786,6 +787,7 @@ def _single_qubit_gates( yield gate(qubit) +@functools.cache def _single_qubit_cliffords() -> Cliffords: X, Y, Z = ( ops.SingleQubitCliffordGate.X, diff --git a/cirq-core/cirq/experiments/qubit_characterizations_test.py b/cirq-core/cirq/experiments/qubit_characterizations_test.py index 733a68e0b0a..36fc6a838d6 100644 --- a/cirq-core/cirq/experiments/qubit_characterizations_test.py +++ b/cirq-core/cirq/experiments/qubit_characterizations_test.py @@ -73,12 +73,35 @@ def check_distinct(unitaries): for p in PAULIS: assert is_pauli(u @ p @ u.conj().T), str(u) - # Check that XZ decomposition has at most one X gate and one Z gate per clifford. + # Check that XZ decomposition has at most one X gate per clifford. for gates in cliffords.c1_in_xz: - num_x = len([gate for gate in gates if gate == cirq.ops.SingleQubitCliffordGate.X]) - num_z = len([gate for gate in gates if gate == cirq.ops.SingleQubitCliffordGate.Z]) + num_i = len([gate for gate in gates if gate == cirq.ops.SingleQubitCliffordGate.I]) + num_x = len( + [ + gate + for gate in gates + if gate + in ( + cirq.ops.SingleQubitCliffordGate.X, + cirq.ops.SingleQubitCliffordGate.X_sqrt, + cirq.ops.SingleQubitCliffordGate.X_nsqrt, + ) + ] + ) + num_z = len( + [ + gate + for gate in gates + if gate + in ( + cirq.ops.SingleQubitCliffordGate.Z, + cirq.ops.SingleQubitCliffordGate.Z_sqrt, + cirq.ops.SingleQubitCliffordGate.Z_nsqrt, + ) + ] + ) + assert num_x + num_z + num_i == len(gates) assert num_x <= 1 - assert num_z <= 1 def test_single_qubit_randomized_benchmarking(): diff --git a/cirq-core/cirq/ops/clifford_gate.py b/cirq-core/cirq/ops/clifford_gate.py index e7cc7d20f9e..47021272a23 100644 --- a/cirq-core/cirq/ops/clifford_gate.py +++ b/cirq-core/cirq/ops/clifford_gate.py @@ -20,7 +20,7 @@ from cirq import protocols, value, linalg, qis from cirq._import import LazyLoader -from cirq._compat import cached_property +from cirq._compat import cached_property, cached_method from cirq.ops import common_gates, named_qubit, raw_types, pauli_gates, phased_x_z_gate from cirq.ops.pauli_gates import Pauli from cirq.type_workarounds import NotImplementedType @@ -777,7 +777,7 @@ def commutes_with_pauli(self, pauli: Pauli) -> bool: to, flip = self.pauli_tuple(pauli) return to == pauli and not flip - @functools.cache + @cached_method def merged_with(self, second: 'SingleQubitCliffordGate') -> 'SingleQubitCliffordGate': """Returns a SingleQubitCliffordGate such that the circuits --output-- and --self--second-- diff --git a/cirq-core/cirq/qis/clifford_tableau.py b/cirq-core/cirq/qis/clifford_tableau.py index 317964d1fa2..e652c669043 100644 --- a/cirq-core/cirq/qis/clifford_tableau.py +++ b/cirq-core/cirq/qis/clifford_tableau.py @@ -17,7 +17,7 @@ import numpy as np from cirq import protocols -from cirq._compat import proper_repr +from cirq._compat import proper_repr, cached_method from cirq.qis import quantum_state_representation from cirq.value import big_endian_int_to_digits, linear_dict, random_state @@ -653,5 +653,6 @@ def measure( ) -> List[int]: return [self._measure(axis, random_state.parse_random_state(seed)) for axis in axes] + @cached_method def __hash__(self) -> int: return hash(self.matrix().tobytes() + self.rs.tobytes()) From 3d958921a0ef56e5f55c01ca8fcb93ed655cabdb Mon Sep 17 00:00:00 2001 From: Nour Yosri Date: Mon, 8 Jan 2024 11:09:20 -0800 Subject: [PATCH 03/12] rerun doctest From 7da2a2b849dbd7923212ed59fa435a6b940d16b3 Mon Sep 17 00:00:00 2001 From: Nour Yosri Date: Mon, 8 Jan 2024 12:08:24 -0800 Subject: [PATCH 04/12] optimize circuit creation --- .../experiments/qubit_characterizations.py | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/cirq-core/cirq/experiments/qubit_characterizations.py b/cirq-core/cirq/experiments/qubit_characterizations.py index b5f8da6e45f..df9331cfd80 100644 --- a/cirq-core/cirq/experiments/qubit_characterizations.py +++ b/cirq-core/cirq/experiments/qubit_characterizations.py @@ -560,11 +560,19 @@ def _measurement(two_qubit_circuit: circuits.Circuit) -> np.ndarray: def _create_parallel_rb_circuit( - qubits: Iterator['cirq.Qid'], num_cliffords: int, c1: list + qubits: Sequence['cirq.Qid'], num_cliffords: int, c1: list ) -> 'cirq.Circuit': - circuits_to_zip = [_random_single_q_clifford(qubit, num_cliffords, c1) for qubit in qubits] - circuit = circuits.Circuit.zip(*circuits_to_zip) - return circuits.Circuit.from_moments(*circuit, ops.measure_each(*qubits)) + sequences_to_zip = [_random_single_q_clifford(qubit, num_cliffords, c1) 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 i in range(len(sequences_to_zip)): + if len(sequences_to_zip[i]) < num_moments: + sequences_to_zip[i].extend( + [ops.SingleQubitCliffordGate.I(qubits[i])] + * (num_moments - len(sequences_to_zip[i])) + ) + moments = tuple(zip(*sequences_to_zip, strict=True)) + return circuits.Circuit.from_moments(*moments, ops.measure_each(*qubits)) def _indices_after_basis_rot(i: int, j: int) -> Tuple[int, Sequence[int], Sequence[int]]: @@ -609,13 +617,12 @@ def _two_qubit_clifford_matrices( def _random_single_q_clifford( qubit: 'cirq.Qid', num_cfds: int, cfds: Sequence[Sequence['cirq.Gate']] -) -> 'cirq.Circuit': +) -> List['cirq.Operation']: clifford_group_size = 24 + ops = [[gate(qubit) for gate in gates] for gates in cfds] gate_ids = list(np.random.choice(clifford_group_size, num_cfds)) - gate_sequence = [gate for gate_id in gate_ids for gate in cfds[gate_id]] - gate_sequence.append(_reduce_gate_seq(gate_sequence) ** -1) - circuit = circuits.Circuit(gate(qubit) for gate in gate_sequence) - return circuit + 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 ops[gate_id]] + [adjoint(qubit)] def _random_two_q_clifford( From 77fc735ec259df2c6928cb22522c83448e28b866 Mon Sep 17 00:00:00 2001 From: Noureldin Date: Mon, 8 Jan 2024 12:22:21 -0800 Subject: [PATCH 05/12] remove `strict=True` option due to incompatibility with python3.9 --- 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 df9331cfd80..c6f8c052998 100644 --- a/cirq-core/cirq/experiments/qubit_characterizations.py +++ b/cirq-core/cirq/experiments/qubit_characterizations.py @@ -571,7 +571,7 @@ def _create_parallel_rb_circuit( [ops.SingleQubitCliffordGate.I(qubits[i])] * (num_moments - len(sequences_to_zip[i])) ) - moments = tuple(zip(*sequences_to_zip, strict=True)) + moments = tuple(zip(*sequences_to_zip)) return circuits.Circuit.from_moments(*moments, ops.measure_each(*qubits)) From b3538df28766a3a5e92a8f0ac398fd91752130f9 Mon Sep 17 00:00:00 2001 From: Nour Yosri Date: Mon, 8 Jan 2024 12:25:22 -0800 Subject: [PATCH 06/12] address comments --- cirq-core/cirq/experiments/qubit_characterizations.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/cirq-core/cirq/experiments/qubit_characterizations.py b/cirq-core/cirq/experiments/qubit_characterizations.py index c6f8c052998..23275f638ae 100644 --- a/cirq-core/cirq/experiments/qubit_characterizations.py +++ b/cirq-core/cirq/experiments/qubit_characterizations.py @@ -565,13 +565,10 @@ def _create_parallel_rb_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. num_moments = max(len(sequence) for sequence in sequences_to_zip) - for i in range(len(sequences_to_zip)): - if len(sequences_to_zip[i]) < num_moments: - sequences_to_zip[i].extend( - [ops.SingleQubitCliffordGate.I(qubits[i])] - * (num_moments - len(sequences_to_zip[i])) - ) - moments = tuple(zip(*sequences_to_zip)) + for q, sequence in zip(qubits, sequences_to_zip): + if (n := len(sequence)) < num_moments: + sequence.extend([ops.SingleQubitCliffordGate.I(q)] * (num_moments - n)) + moments = zip(*sequences_to_zip) return circuits.Circuit.from_moments(*moments, ops.measure_each(*qubits)) From 7f7a94c047f880fedc5a6869f2df57d08019f909 Mon Sep 17 00:00:00 2001 From: Nour Yosri Date: Mon, 8 Jan 2024 12:31:28 -0800 Subject: [PATCH 07/12] types --- cirq-core/cirq/experiments/qubit_characterizations.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cirq-core/cirq/experiments/qubit_characterizations.py b/cirq-core/cirq/experiments/qubit_characterizations.py index 23275f638ae..342d03bde80 100644 --- a/cirq-core/cirq/experiments/qubit_characterizations.py +++ b/cirq-core/cirq/experiments/qubit_characterizations.py @@ -257,7 +257,7 @@ def single_qubit_randomized_benchmarking( A RandomizedBenchMarkResult object that stores and plots the result. """ - qubits = cast(Iterator['cirq.Qid'], (qubit,)) + qubits = (qubit,) result = parallel_single_qubit_randomized_benchmarking( sampler, qubits, @@ -271,7 +271,7 @@ def single_qubit_randomized_benchmarking( def parallel_single_qubit_randomized_benchmarking( sampler: 'cirq.Sampler', - qubits: Iterator['cirq.Qid'], + qubits: Sequence['cirq.Qid'], use_xy_basis: bool = True, *, num_clifford_range: Sequence[int] = tuple( From c43fabf50063595ebde1efe93d995617063d0114 Mon Sep 17 00:00:00 2001 From: Nour Yosri Date: Mon, 8 Jan 2024 13:02:46 -0800 Subject: [PATCH 08/12] rename variable --- cirq-core/cirq/experiments/qubit_characterizations.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cirq-core/cirq/experiments/qubit_characterizations.py b/cirq-core/cirq/experiments/qubit_characterizations.py index 342d03bde80..7ea0269ee93 100644 --- a/cirq-core/cirq/experiments/qubit_characterizations.py +++ b/cirq-core/cirq/experiments/qubit_characterizations.py @@ -616,10 +616,10 @@ def _random_single_q_clifford( qubit: 'cirq.Qid', num_cfds: int, cfds: Sequence[Sequence['cirq.Gate']] ) -> List['cirq.Operation']: clifford_group_size = 24 - ops = [[gate(qubit) for gate in gates] for gates in cfds] + operations = [[gate(qubit) for gate in gates] for gates in cfds] gate_ids = list(np.random.choice(clifford_group_size, num_cfds)) 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 ops[gate_id]] + [adjoint(qubit)] + return [op for gate_id in gate_ids for op in operations[gate_id]] + [adjoint(qubit)] def _random_two_q_clifford( From 4dfe491fe3d11dabb0f52638012dd1b5e15b11c1 Mon Sep 17 00:00:00 2001 From: Nour Yosri Date: Mon, 8 Jan 2024 13:28:42 -0800 Subject: [PATCH 09/12] normalize state vector in test --- cirq-core/cirq/sim/clifford/clifford_simulator_test.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cirq-core/cirq/sim/clifford/clifford_simulator_test.py b/cirq-core/cirq/sim/clifford/clifford_simulator_test.py index 33232e28518..51823830e8b 100644 --- a/cirq-core/cirq/sim/clifford/clifford_simulator_test.py +++ b/cirq-core/cirq/sim/clifford/clifford_simulator_test.py @@ -323,10 +323,11 @@ def test_clifford_circuit_SHSYSHS(): clifford_simulator = cirq.CliffordSimulator() state_vector_simulator = cirq.Simulator() - + want = state_vector_simulator.simulate(circuit).final_state_vector + want /= np.sqrt(np.linalg.norm(want)) np.testing.assert_almost_equal( clifford_simulator.simulate(circuit).final_state.state_vector(), - state_vector_simulator.simulate(circuit).final_state_vector, + want, ) From 3ac412c9b0327aa4e653b3a2c1efb6d33b0106c6 Mon Sep 17 00:00:00 2001 From: Nour Yosri Date: Mon, 8 Jan 2024 13:30:04 -0800 Subject: [PATCH 10/12] add comment --- cirq-core/cirq/sim/clifford/clifford_simulator_test.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/cirq-core/cirq/sim/clifford/clifford_simulator_test.py b/cirq-core/cirq/sim/clifford/clifford_simulator_test.py index 51823830e8b..84aa388ce4f 100644 --- a/cirq-core/cirq/sim/clifford/clifford_simulator_test.py +++ b/cirq-core/cirq/sim/clifford/clifford_simulator_test.py @@ -323,11 +323,12 @@ def test_clifford_circuit_SHSYSHS(): clifford_simulator = cirq.CliffordSimulator() state_vector_simulator = cirq.Simulator() - want = state_vector_simulator.simulate(circuit).final_state_vector - want /= np.sqrt(np.linalg.norm(want)) + + # workaround until #6402 is resolved. + final_state_vector = state_vector_simulator.simulate(circuit).final_state_vector + final_state_vector /= np.sqrt(np.linalg.norm(final_state_vector)) np.testing.assert_almost_equal( - clifford_simulator.simulate(circuit).final_state.state_vector(), - want, + clifford_simulator.simulate(circuit).final_state.state_vector(), final_state_vector ) From 8099cfda98d4cc22da1a92b2ff4964e6971b5e5f Mon Sep 17 00:00:00 2001 From: Nour Yosri Date: Mon, 8 Jan 2024 14:26:24 -0800 Subject: [PATCH 11/12] nit --- 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 7ea0269ee93..65967154395 100644 --- a/cirq-core/cirq/experiments/qubit_characterizations.py +++ b/cirq-core/cirq/experiments/qubit_characterizations.py @@ -257,10 +257,9 @@ def single_qubit_randomized_benchmarking( A RandomizedBenchMarkResult object that stores and plots the result. """ - qubits = (qubit,) result = parallel_single_qubit_randomized_benchmarking( sampler, - qubits, + (qubit,), use_xy_basis, num_clifford_range=num_clifford_range, num_circuits=num_circuits, From 0458700b4b687e6e33d034cce6fbd5ab457b0325 Mon Sep 17 00:00:00 2001 From: Nour Yosri Date: Mon, 8 Jan 2024 14:28:49 -0800 Subject: [PATCH 12/12] rerun test