-
Notifications
You must be signed in to change notification settings - Fork 1.1k
make SingleQubitCliffordGate immutable singletons and use it in qubit_characterizations for a 37% speedup #6392
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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, 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 | ||
|
@@ -356,6 +358,8 @@ def _get_sqrt_map( | |
class CliffordGate(raw_types.Gate, CommonCliffordGates): | ||
"""Clifford rotation for N-qubit.""" | ||
|
||
_clifford_tableau: qis.CliffordTableau | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: We can move this to line 382 as
|
||
|
||
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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What does making this a dataclass actually do here with all these options set to False? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. makes the class frozen while not overriding any method. the default parameters will create an |
||
@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 | ||
|
||
@cached_method | ||
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): | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
|
||
|
@@ -652,3 +652,7 @@ 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] | ||
|
||
@cached_method | ||
def __hash__(self) -> int: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. consider making this a cached_method as well. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
return hash(self.matrix().tobytes() + self.rs.tobytes()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NIT: Should we cache
_single_qubit_cliffords
as well?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done