-
Notifications
You must be signed in to change notification settings - Fork 1.2k
All single-qubit Cliffords #5584
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
10ef9e4
219ea04
154268d
e2f2827
d02fdb7
c44df1f
c60eb37
8fa68a4
05fca46
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 |
|---|---|---|
|
|
@@ -20,7 +20,7 @@ | |
|
|
||
| from cirq import _compat, protocols, value, linalg, qis | ||
| from cirq._import import LazyLoader | ||
| from cirq.ops import common_gates, identity, named_qubit, raw_types, pauli_gates, phased_x_z_gate | ||
| 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 | ||
|
|
||
|
|
@@ -158,121 +158,131 @@ class CommonCliffordGateMetaClass(value.ABCMetaImplementAnyOneOf): | |
| # Note that in python 3.9+ @classmethod can be used with @property, so these | ||
| # can be moved to CommonCliffordGates. | ||
|
|
||
| @property | ||
| def all_single_qubit_cliffords(cls) -> Sequence['cirq.SingleQubitCliffordGate']: | ||
| """All 24 single-qubit Clifford gates.""" | ||
| if not hasattr(cls, '_all_single_qubit_cliffords'): | ||
| pX = (pauli_gates.X, False) | ||
| mX = (pauli_gates.X, True) | ||
| pY = (pauli_gates.Y, False) | ||
| mY = (pauli_gates.Y, True) | ||
| pZ = (pauli_gates.Z, False) | ||
| mZ = (pauli_gates.Z, True) | ||
|
|
||
| def from_xz(x_to, z_to): | ||
| return SingleQubitCliffordGate.from_clifford_tableau( | ||
| _to_clifford_tableau(x_to=x_to, z_to=z_to) | ||
| ) | ||
|
|
||
| # Order in is relied on in properties that retrieve a specific Clifford below. | ||
| cls._all_single_qubit_cliffords = ( | ||
| # 0: Identity | ||
| from_xz(x_to=pX, z_to=pZ), # I | ||
| # 1..3: Paulis | ||
| from_xz(x_to=pX, z_to=mZ), # X | ||
| from_xz(x_to=mX, z_to=mZ), # Y | ||
| from_xz(x_to=mX, z_to=pZ), # Z | ||
| # 4..6: Square roots of Paulis | ||
| from_xz(x_to=pX, z_to=mY), # I-iX | ||
| from_xz(x_to=mZ, z_to=pX), # I-iY | ||
| from_xz(x_to=pY, z_to=pZ), # I-iZ aka S | ||
| # 7..9: Negative square roots of Paulis | ||
| from_xz(x_to=pX, z_to=pY), # I+iX | ||
| from_xz(x_to=pZ, z_to=mX), # I+iY | ||
| from_xz(x_to=mY, z_to=pZ), # I+iZ | ||
| # 10..15: Hadamards | ||
| from_xz(x_to=pZ, z_to=pX), # Z+X aka H | ||
| from_xz(x_to=pY, z_to=mZ), # X+Y | ||
| from_xz(x_to=mX, z_to=pY), # Y+Z | ||
| from_xz(x_to=mZ, z_to=mX), # Z-X | ||
| from_xz(x_to=mY, z_to=mZ), # X-Y | ||
| from_xz(x_to=mX, z_to=mY), # Y-Z | ||
| # 16..23: Order-3 Cliffords | ||
| from_xz(x_to=pY, z_to=pX), # I-i(+X+Y+Z) | ||
| from_xz(x_to=mZ, z_to=mY), # I-i(+X+Y-Z) | ||
| from_xz(x_to=pZ, z_to=mY), # I-i(+X-Y+Z) | ||
| from_xz(x_to=mY, z_to=mX), # I-i(+X-Y-Z) | ||
| from_xz(x_to=mZ, z_to=pY), # I-i(-X+Y+Z) | ||
| from_xz(x_to=mY, z_to=pX), # I-i(-X+Y-Z) | ||
| from_xz(x_to=pY, z_to=mX), # I-i(-X-Y+Z) | ||
| from_xz(x_to=pZ, z_to=pY), # I-i(-X-Y-Z) | ||
| ) | ||
| return cls._all_single_qubit_cliffords | ||
|
|
||
| @property | ||
| def I(cls) -> 'cirq.SingleQubitCliffordGate': | ||
| if not hasattr(cls, '_I'): | ||
| cls._I = SingleQubitCliffordGate.from_clifford_tableau(_gate_tableau(1, identity.I)) | ||
| return cls._I | ||
| return cls.all_single_qubit_cliffords[0] | ||
|
|
||
| @property | ||
| def X(cls) -> 'cirq.SingleQubitCliffordGate': | ||
| if not hasattr(cls, '_X'): | ||
| cls._X = SingleQubitCliffordGate.from_clifford_tableau(_gate_tableau(1, pauli_gates.X)) | ||
| return cls._X | ||
| return cls.all_single_qubit_cliffords[1] | ||
|
|
||
| @property | ||
| def Y(cls) -> 'cirq.SingleQubitCliffordGate': | ||
| if not hasattr(cls, '_Y'): | ||
| cls._Y = SingleQubitCliffordGate.from_clifford_tableau(_gate_tableau(1, pauli_gates.Y)) | ||
| return cls._Y | ||
| return cls.all_single_qubit_cliffords[2] | ||
|
|
||
| @property | ||
| def Z(cls) -> 'cirq.SingleQubitCliffordGate': | ||
| if not hasattr(cls, '_Z'): | ||
| cls._Z = SingleQubitCliffordGate.from_clifford_tableau(_gate_tableau(1, pauli_gates.Z)) | ||
| return cls._Z | ||
| return cls.all_single_qubit_cliffords[3] | ||
|
|
||
| @property | ||
| def H(cls) -> 'cirq.SingleQubitCliffordGate': | ||
| if not hasattr(cls, '_H'): | ||
| cls._H = SingleQubitCliffordGate.from_clifford_tableau(_gate_tableau(1, common_gates.H)) | ||
| return cls._H | ||
| return cls.all_single_qubit_cliffords[10] | ||
|
|
||
| @property | ||
| def S(cls) -> 'cirq.SingleQubitCliffordGate': | ||
| if not hasattr(cls, '_S'): | ||
| cls._S = SingleQubitCliffordGate.from_clifford_tableau(_gate_tableau(1, common_gates.S)) | ||
| return cls._S | ||
| return cls.all_single_qubit_cliffords[6] | ||
|
|
||
| @property | ||
| def CNOT(cls) -> 'cirq.CliffordGate': | ||
| if not hasattr(cls, '_CNOT'): | ||
| cls._CNOT = CliffordGate.from_clifford_tableau(_gate_tableau(2, common_gates.CNOT)) | ||
| t = qis.CliffordTableau(num_qubits=2) | ||
| t.xs = [[1, 1], [0, 1], [0, 0], [0, 0]] | ||
| t.zs = [[0, 0], [0, 0], [1, 0], [1, 1]] | ||
| cls._CNOT = CliffordGate.from_clifford_tableau(t) | ||
| return cls._CNOT | ||
|
|
||
| @property | ||
| def CZ(cls) -> 'cirq.CliffordGate': | ||
| if not hasattr(cls, '_CZ'): | ||
| cls._CZ = CliffordGate.from_clifford_tableau(_gate_tableau(2, common_gates.CZ)) | ||
| t = qis.CliffordTableau(num_qubits=2) | ||
| t.xs = [[1, 0], [0, 1], [0, 0], [0, 0]] | ||
| t.zs = [[0, 1], [1, 0], [1, 0], [0, 1]] | ||
| cls._CZ = CliffordGate.from_clifford_tableau(t) | ||
| return cls._CZ | ||
|
|
||
| @property | ||
| def SWAP(cls) -> 'cirq.CliffordGate': | ||
| if not hasattr(cls, '_SWAP'): | ||
| cls._SWAP = CliffordGate.from_clifford_tableau(_gate_tableau(2, common_gates.SWAP)) | ||
| t = qis.CliffordTableau(num_qubits=2) | ||
| t.xs = [[0, 1], [1, 0], [0, 0], [0, 0]] | ||
| t.zs = [[0, 0], [0, 0], [0, 1], [1, 0]] | ||
| cls._SWAP = CliffordGate.from_clifford_tableau(t) | ||
| return cls._SWAP | ||
|
|
||
| @property | ||
| def X_sqrt(cls) -> 'cirq.SingleQubitCliffordGate': | ||
| if not hasattr(cls, '_X_sqrt'): | ||
| # Unfortunately, due the code style, the matrix should be viewed transposed. | ||
| # Note xs, zs, and rs are column vector. | ||
| # Transformation: X -> X, Z -> -Y | ||
| clifford_tableau = qis.CliffordTableau._from_json_dict_( | ||
| n=1, rs=[0, 1], xs=[[1], [1]], zs=[[0], [1]] | ||
| ) | ||
| cls._X_sqrt = SingleQubitCliffordGate.from_clifford_tableau(clifford_tableau) | ||
| return cls._X_sqrt | ||
| return cls.all_single_qubit_cliffords[4] | ||
|
|
||
| @property | ||
| def X_nsqrt(cls) -> 'cirq.SingleQubitCliffordGate': | ||
| if not hasattr(cls, '_X_nsqrt'): | ||
| # Transformation: X->X, Z->Y | ||
| clifford_tableau = qis.CliffordTableau._from_json_dict_( | ||
| n=1, rs=[0, 0], xs=[[1], [1]], zs=[[0], [1]] | ||
| ) | ||
| cls._X_nsqrt = SingleQubitCliffordGate.from_clifford_tableau(clifford_tableau) | ||
| return cls._X_nsqrt | ||
| return cls.all_single_qubit_cliffords[7] | ||
|
|
||
| @property | ||
| def Y_sqrt(cls) -> 'cirq.SingleQubitCliffordGate': | ||
| if not hasattr(cls, '_Y_sqrt'): | ||
| # Transformation: X -> -Z, Z -> X | ||
| clifford_tableau = qis.CliffordTableau._from_json_dict_( | ||
| n=1, rs=[1, 0], xs=[[0], [1]], zs=[[1], [0]] | ||
| ) | ||
| cls._Y_sqrt = SingleQubitCliffordGate.from_clifford_tableau(clifford_tableau) | ||
| return cls._Y_sqrt | ||
| return cls.all_single_qubit_cliffords[5] | ||
|
|
||
| @property | ||
| def Y_nsqrt(cls) -> 'cirq.SingleQubitCliffordGate': | ||
| if not hasattr(cls, '_Y_nsqrt'): | ||
| # Transformation: X -> Z, Z -> -X | ||
| clifford_tableau = qis.CliffordTableau._from_json_dict_( | ||
| n=1, rs=[0, 1], xs=[[0], [1]], zs=[[1], [0]] | ||
| ) | ||
| cls._Y_nsqrt = SingleQubitCliffordGate.from_clifford_tableau(clifford_tableau) | ||
| return cls._Y_nsqrt | ||
| return cls.all_single_qubit_cliffords[8] | ||
|
|
||
| @property | ||
| def Z_sqrt(cls) -> 'cirq.SingleQubitCliffordGate': | ||
| if not hasattr(cls, '_Z_sqrt'): | ||
| # Transformation: X -> Y, Z -> Z | ||
| _clifford_tableau = qis.CliffordTableau._from_json_dict_( | ||
| n=1, rs=[0, 0], xs=[[1], [0]], zs=[[1], [1]] | ||
| ) | ||
| cls._Z_sqrt = SingleQubitCliffordGate.from_clifford_tableau(_clifford_tableau) | ||
| return cls._Z_sqrt | ||
| return cls.all_single_qubit_cliffords[6] | ||
|
Collaborator
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. This is the same as Can you perhaps add an independent test that class-property gates have correct values?
Collaborator
Author
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. The S gate and Z_sqrt are the same gate. Square the S gate to convince yourself of that. The PR does include independent test (test_all_single_qubit_clifford_unitaries) that checks all the gates have correct values. It is already order-sensitive. |
||
|
|
||
| @property | ||
| def Z_nsqrt(cls) -> 'cirq.SingleQubitCliffordGate': | ||
| if not hasattr(cls, '_Z_nsqrt'): | ||
| # Transformation: X -> -Y, Z -> Z | ||
| _clifford_tableau = qis.CliffordTableau._from_json_dict_( | ||
| n=1, rs=[1, 0], xs=[[1], [0]], zs=[[1], [1]] | ||
| ) | ||
| cls._Z_nsqrt = SingleQubitCliffordGate.from_clifford_tableau(_clifford_tableau) | ||
| return cls._Z_nsqrt | ||
| return cls.all_single_qubit_cliffords[9] | ||
|
|
||
|
|
||
| class CommonCliffordGates(metaclass=CommonCliffordGateMetaClass): | ||
|
|
||
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.
I'm not stoked about these indices which could get out of sync with the tuple of all gates, but I don't have a great alternative. At very least, we should document that the order of the all gates tuple should not be changed without updating these indices.
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.
Also not a big fan of the constants. An alternative I considered was to use a Dict[str, ...], but it involves making up names for all Clifford gates most of which would be highly non-standard.
I imagine that anyone wanting access to a specific gate would use a property (e.g. CliffordGate.X_sqrt) and folks would use all_single_qubit_cliffords if they really want to get all 24. Here's to wishful thinking :-)
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.
WDYT?
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.
SGTM
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.
Perhaps a namedtuple could help here. You could use property names for the items that should be accessible as properties and a say repeated
_12name with therename=Trueargument for all other gates for which you don't care about the name.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.
AFAICT, the primary (only?) use-case for all_single_qubit_cliffords is the situation where someone wants to iterate over all single-qubit Cliffords, e.g. to execute a test-case on all of them. I think using namedtuple adds unnecessary clutter to output and is potentially confusing.