From 05a1efef6f93fc8b81b2fba8d40bcc5f8422de04 Mon Sep 17 00:00:00 2001 From: "Michael A. Perlin" Date: Fri, 16 Aug 2024 17:12:44 -0500 Subject: [PATCH 01/14] faster commutation --- cirq-core/cirq/ops/pauli_string.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cirq-core/cirq/ops/pauli_string.py b/cirq-core/cirq/ops/pauli_string.py index 1978b6ba031..4bab9ff0572 100644 --- a/cirq-core/cirq/ops/pauli_string.py +++ b/cirq-core/cirq/ops/pauli_string.py @@ -1395,8 +1395,8 @@ def inplace_after(self, ops: 'cirq.OP_TREE') -> 'cirq.MutablePauliString': p1 = _INT_TO_PAULI_OR_IDENTITY[ps[1]] # Kick across Paulis that anti-commute with the controls. - kickback_0_to_1 = not protocols.commutes(p0, gate.pauli0) - kickback_1_to_0 = not protocols.commutes(p1, gate.pauli1) + kickback_0_to_1 = not (identity.I in [p0, gate.pauli0] or p0 == gate.pauli0) + kickback_1_to_0 = not (identity.I in [p1, gate.pauli1] or p1 == gate.pauli1) kick0 = gate.pauli1 if kickback_0_to_1 else identity.I kick1 = gate.pauli0 if kickback_1_to_0 else identity.I self.__imul__({q0: p0, q1: kick0}) From 49104f46857729163b85f54e64042e11bdea5665 Mon Sep 17 00:00:00 2001 From: "Michael A. Perlin" Date: Fri, 16 Aug 2024 17:47:56 -0500 Subject: [PATCH 02/14] add _paulis_commute --- cirq-core/cirq/ops/pauli_string.py | 22 +++++++++++++++++----- cirq-core/cirq/ops/pauli_string_test.py | 8 ++++++++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/cirq-core/cirq/ops/pauli_string.py b/cirq-core/cirq/ops/pauli_string.py index 4bab9ff0572..d850ff83a74 100644 --- a/cirq-core/cirq/ops/pauli_string.py +++ b/cirq-core/cirq/ops/pauli_string.py @@ -783,7 +783,7 @@ def _commutes_( ) -> Union[bool, NotImplementedType, None]: if not isinstance(other, PauliString): return NotImplemented - return sum(not protocols.commutes(p0, p1) for p0, p1 in self.zip_paulis(other)) % 2 == 0 + return sum(not _paulis_commute(p0, p1) for p0, p1 in self.zip_paulis(other)) % 2 == 0 def __neg__(self) -> 'PauliString': return PauliString(qubit_pauli_map=self._qubit_pauli_map, coefficient=-self._coefficient) @@ -1080,6 +1080,18 @@ def _resolve_parameters_( return PauliString(qubit_pauli_map=self._qubit_pauli_map, coefficient=coefficient) +def _paulis_commute( + p0: Union['cirq.Pauli', 'cirq.IdentityGate'], p1: Union['cirq.Pauli', 'cirq.IdentityGate'] +) -> bool: + """Determines whether two single-qubit Pauli operators commute. + + Args: + p0: A single-qubit Pauli operator. + p1: A single-qubit Pauli operator. + """ + return p0 == identity.I or p1 == identity.I or p0 == p1 + + def _validate_qubit_mapping( qubit_map: Mapping[TKey, int], pauli_qubits: Tuple[TKey, ...], num_state_qubits: int ) -> None: @@ -1395,8 +1407,8 @@ def inplace_after(self, ops: 'cirq.OP_TREE') -> 'cirq.MutablePauliString': p1 = _INT_TO_PAULI_OR_IDENTITY[ps[1]] # Kick across Paulis that anti-commute with the controls. - kickback_0_to_1 = not (identity.I in [p0, gate.pauli0] or p0 == gate.pauli0) - kickback_1_to_0 = not (identity.I in [p1, gate.pauli1] or p1 == gate.pauli1) + kickback_0_to_1 = not _paulis_commute(p0, gate.pauli0) + kickback_1_to_0 = not _paulis_commute(p1, gate.pauli1) kick0 = gate.pauli1 if kickback_0_to_1 else identity.I kick1 = gate.pauli0 if kickback_1_to_0 else identity.I self.__imul__({q0: p0, q1: kick0}) @@ -1673,11 +1685,11 @@ def merge_and_kickback( return int(inv) * 2 - 1 quarter_kickback = 0 - if qubit0 in pauli_map and not protocols.commutes(pauli_map[qubit0], gate.pauli0): + if qubit0 in pauli_map and not _paulis_commute(pauli_map[qubit0], gate.pauli0): quarter_kickback += merge_and_kickback( qubit1, gate.pauli1, pauli_map.get(qubit1), gate.invert1 ) - if qubit1 in pauli_map and not protocols.commutes(pauli_map[qubit1], gate.pauli1): + if qubit1 in pauli_map and not _paulis_commute(pauli_map[qubit1], gate.pauli1): quarter_kickback += merge_and_kickback( qubit0, pauli_map.get(qubit0), gate.pauli0, gate.invert0 ) diff --git a/cirq-core/cirq/ops/pauli_string_test.py b/cirq-core/cirq/ops/pauli_string_test.py index 48ea085f311..57e9d16645b 100644 --- a/cirq-core/cirq/ops/pauli_string_test.py +++ b/cirq-core/cirq/ops/pauli_string_test.py @@ -2023,3 +2023,11 @@ def test_resolve(resolve_fn): pst = cirq.PauliString({q: 'x'}, coefficient=t) ps1 = cirq.PauliString({q: 'x'}, coefficient=1j) assert resolve_fn(pst, {'t': 1j}) == ps1 + + +def test_paulis_commute(): + assert all( + cirq.ops.pauli_string._paulis_commute(p0, p1) == cirq.commutes(p0, p1) + for p0 in [cirq.I, cirq.X, cirq.Y, cirq.Z] + for p1 in [cirq.I, cirq.X, cirq.Y, cirq.Z] + ) From 06a29e4b65ab4e91fb675ad0840dc7bd3f69a733 Mon Sep 17 00:00:00 2001 From: "Michael A. Perlin" Date: Fri, 16 Aug 2024 23:02:13 -0500 Subject: [PATCH 03/14] Revert "add _paulis_commute" This reverts commit 49104f46857729163b85f54e64042e11bdea5665. --- cirq-core/cirq/ops/pauli_string.py | 22 +++++----------------- cirq-core/cirq/ops/pauli_string_test.py | 8 -------- 2 files changed, 5 insertions(+), 25 deletions(-) diff --git a/cirq-core/cirq/ops/pauli_string.py b/cirq-core/cirq/ops/pauli_string.py index d850ff83a74..4bab9ff0572 100644 --- a/cirq-core/cirq/ops/pauli_string.py +++ b/cirq-core/cirq/ops/pauli_string.py @@ -783,7 +783,7 @@ def _commutes_( ) -> Union[bool, NotImplementedType, None]: if not isinstance(other, PauliString): return NotImplemented - return sum(not _paulis_commute(p0, p1) for p0, p1 in self.zip_paulis(other)) % 2 == 0 + return sum(not protocols.commutes(p0, p1) for p0, p1 in self.zip_paulis(other)) % 2 == 0 def __neg__(self) -> 'PauliString': return PauliString(qubit_pauli_map=self._qubit_pauli_map, coefficient=-self._coefficient) @@ -1080,18 +1080,6 @@ def _resolve_parameters_( return PauliString(qubit_pauli_map=self._qubit_pauli_map, coefficient=coefficient) -def _paulis_commute( - p0: Union['cirq.Pauli', 'cirq.IdentityGate'], p1: Union['cirq.Pauli', 'cirq.IdentityGate'] -) -> bool: - """Determines whether two single-qubit Pauli operators commute. - - Args: - p0: A single-qubit Pauli operator. - p1: A single-qubit Pauli operator. - """ - return p0 == identity.I or p1 == identity.I or p0 == p1 - - def _validate_qubit_mapping( qubit_map: Mapping[TKey, int], pauli_qubits: Tuple[TKey, ...], num_state_qubits: int ) -> None: @@ -1407,8 +1395,8 @@ def inplace_after(self, ops: 'cirq.OP_TREE') -> 'cirq.MutablePauliString': p1 = _INT_TO_PAULI_OR_IDENTITY[ps[1]] # Kick across Paulis that anti-commute with the controls. - kickback_0_to_1 = not _paulis_commute(p0, gate.pauli0) - kickback_1_to_0 = not _paulis_commute(p1, gate.pauli1) + kickback_0_to_1 = not (identity.I in [p0, gate.pauli0] or p0 == gate.pauli0) + kickback_1_to_0 = not (identity.I in [p1, gate.pauli1] or p1 == gate.pauli1) kick0 = gate.pauli1 if kickback_0_to_1 else identity.I kick1 = gate.pauli0 if kickback_1_to_0 else identity.I self.__imul__({q0: p0, q1: kick0}) @@ -1685,11 +1673,11 @@ def merge_and_kickback( return int(inv) * 2 - 1 quarter_kickback = 0 - if qubit0 in pauli_map and not _paulis_commute(pauli_map[qubit0], gate.pauli0): + if qubit0 in pauli_map and not protocols.commutes(pauli_map[qubit0], gate.pauli0): quarter_kickback += merge_and_kickback( qubit1, gate.pauli1, pauli_map.get(qubit1), gate.invert1 ) - if qubit1 in pauli_map and not _paulis_commute(pauli_map[qubit1], gate.pauli1): + if qubit1 in pauli_map and not protocols.commutes(pauli_map[qubit1], gate.pauli1): quarter_kickback += merge_and_kickback( qubit0, pauli_map.get(qubit0), gate.pauli0, gate.invert0 ) diff --git a/cirq-core/cirq/ops/pauli_string_test.py b/cirq-core/cirq/ops/pauli_string_test.py index 57e9d16645b..48ea085f311 100644 --- a/cirq-core/cirq/ops/pauli_string_test.py +++ b/cirq-core/cirq/ops/pauli_string_test.py @@ -2023,11 +2023,3 @@ def test_resolve(resolve_fn): pst = cirq.PauliString({q: 'x'}, coefficient=t) ps1 = cirq.PauliString({q: 'x'}, coefficient=1j) assert resolve_fn(pst, {'t': 1j}) == ps1 - - -def test_paulis_commute(): - assert all( - cirq.ops.pauli_string._paulis_commute(p0, p1) == cirq.commutes(p0, p1) - for p0 in [cirq.I, cirq.X, cirq.Y, cirq.Z] - for p1 in [cirq.I, cirq.X, cirq.Y, cirq.Z] - ) From 87b83162c36a6d083cdc66189930e275d6d82046 Mon Sep 17 00:00:00 2001 From: "Michael A. Perlin" Date: Fri, 16 Aug 2024 23:02:14 -0500 Subject: [PATCH 04/14] Revert "faster commutation" This reverts commit 05a1efef6f93fc8b81b2fba8d40bcc5f8422de04. --- cirq-core/cirq/ops/pauli_string.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cirq-core/cirq/ops/pauli_string.py b/cirq-core/cirq/ops/pauli_string.py index 4bab9ff0572..1978b6ba031 100644 --- a/cirq-core/cirq/ops/pauli_string.py +++ b/cirq-core/cirq/ops/pauli_string.py @@ -1395,8 +1395,8 @@ def inplace_after(self, ops: 'cirq.OP_TREE') -> 'cirq.MutablePauliString': p1 = _INT_TO_PAULI_OR_IDENTITY[ps[1]] # Kick across Paulis that anti-commute with the controls. - kickback_0_to_1 = not (identity.I in [p0, gate.pauli0] or p0 == gate.pauli0) - kickback_1_to_0 = not (identity.I in [p1, gate.pauli1] or p1 == gate.pauli1) + kickback_0_to_1 = not protocols.commutes(p0, gate.pauli0) + kickback_1_to_0 = not protocols.commutes(p1, gate.pauli1) kick0 = gate.pauli1 if kickback_0_to_1 else identity.I kick1 = gate.pauli0 if kickback_1_to_0 else identity.I self.__imul__({q0: p0, q1: kick0}) From 65afb2a5680ae0b3fab51a8bb25c807752f88a45 Mon Sep 17 00:00:00 2001 From: "Michael A. Perlin" Date: Fri, 16 Aug 2024 23:05:04 -0500 Subject: [PATCH 05/14] add IdentityGate._commutes_ --- cirq-core/cirq/ops/identity.py | 3 +++ cirq-core/cirq/ops/identity_test.py | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/cirq-core/cirq/ops/identity.py b/cirq-core/cirq/ops/identity.py index c13d8208620..27b8b7c2a6b 100644 --- a/cirq-core/cirq/ops/identity.py +++ b/cirq-core/cirq/ops/identity.py @@ -75,6 +75,9 @@ def __pow__(self, power: Any) -> Any: return self return NotImplemented + def _commutes_(self, other: Any, *, atol: float = 1e-8) -> bool: + return True + def _has_unitary_(self) -> bool: return True diff --git a/cirq-core/cirq/ops/identity_test.py b/cirq-core/cirq/ops/identity_test.py index a3ce014b293..5a20fc81235 100644 --- a/cirq-core/cirq/ops/identity_test.py +++ b/cirq-core/cirq/ops/identity_test.py @@ -208,3 +208,7 @@ def test_identity_short_circuits_act_on(): args = mock.Mock(cirq.SimulationState) args._act_on_fallback_.side_effect = mock.Mock(side_effect=Exception('No!')) cirq.act_on(cirq.IdentityGate(1)(cirq.LineQubit(0)), args) + + +def test_identity_commutes(): + assert cirq.I._commutes_("other") From caf76b5fd07f559e39afdcb67a5230a849b3532a Mon Sep 17 00:00:00 2001 From: "Michael A. Perlin" Date: Fri, 16 Aug 2024 23:05:57 -0500 Subject: [PATCH 06/14] add comment --- cirq-core/cirq/ops/identity.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cirq-core/cirq/ops/identity.py b/cirq-core/cirq/ops/identity.py index 27b8b7c2a6b..3c9597a0a3e 100644 --- a/cirq-core/cirq/ops/identity.py +++ b/cirq-core/cirq/ops/identity.py @@ -76,6 +76,7 @@ def __pow__(self, power: Any) -> Any: return NotImplemented def _commutes_(self, other: Any, *, atol: float = 1e-8) -> bool: + """The identity gate commutes with everything.""" return True def _has_unitary_(self) -> bool: From 0f9b3524a6206de1ccd1a8935252caefcb7d30b1 Mon Sep 17 00:00:00 2001 From: "Michael A. Perlin" Date: Fri, 16 Aug 2024 23:08:18 -0500 Subject: [PATCH 07/14] commute with gates only --- cirq-core/cirq/ops/identity.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cirq-core/cirq/ops/identity.py b/cirq-core/cirq/ops/identity.py index 3c9597a0a3e..9df805be5e6 100644 --- a/cirq-core/cirq/ops/identity.py +++ b/cirq-core/cirq/ops/identity.py @@ -76,7 +76,9 @@ def __pow__(self, power: Any) -> Any: return NotImplemented def _commutes_(self, other: Any, *, atol: float = 1e-8) -> bool: - """The identity gate commutes with everything.""" + """The identity gate commutes with all other gates.""" + if not isinstance(other, raw_types.Gate): + return NotImplemented return True def _has_unitary_(self) -> bool: From 68e5755803dde0a8554a0bafe9309a2681bc0c65 Mon Sep 17 00:00:00 2001 From: "Michael A. Perlin" Date: Fri, 16 Aug 2024 23:11:33 -0500 Subject: [PATCH 08/14] fix test --- cirq-core/cirq/ops/identity_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cirq-core/cirq/ops/identity_test.py b/cirq-core/cirq/ops/identity_test.py index 5a20fc81235..1fcf18d18de 100644 --- a/cirq-core/cirq/ops/identity_test.py +++ b/cirq-core/cirq/ops/identity_test.py @@ -211,4 +211,4 @@ def test_identity_short_circuits_act_on(): def test_identity_commutes(): - assert cirq.I._commutes_("other") + assert cirq.I._commutes_(cirq.X) From 3b979e392c07f3801239c9883cdb5fb2e1405d15 Mon Sep 17 00:00:00 2001 From: "Michael A. Perlin" Date: Fri, 16 Aug 2024 23:34:30 -0500 Subject: [PATCH 09/14] fix test --- cirq-core/cirq/ops/identity_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cirq-core/cirq/ops/identity_test.py b/cirq-core/cirq/ops/identity_test.py index 1fcf18d18de..ca83136450d 100644 --- a/cirq-core/cirq/ops/identity_test.py +++ b/cirq-core/cirq/ops/identity_test.py @@ -212,3 +212,4 @@ def test_identity_short_circuits_act_on(): def test_identity_commutes(): assert cirq.I._commutes_(cirq.X) + assert cirq.I._commutes_("placeholder") is NotImplemented From 066d7282526fa5379c42ed130c670970a980a688 Mon Sep 17 00:00:00 2001 From: "Michael A. Perlin" Date: Sun, 18 Aug 2024 11:10:15 -0500 Subject: [PATCH 10/14] Update cirq-core/cirq/ops/identity_test.py Co-authored-by: Pavol Juhas --- cirq-core/cirq/ops/identity_test.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cirq-core/cirq/ops/identity_test.py b/cirq-core/cirq/ops/identity_test.py index ca83136450d..11a2a21a024 100644 --- a/cirq-core/cirq/ops/identity_test.py +++ b/cirq-core/cirq/ops/identity_test.py @@ -211,5 +211,6 @@ def test_identity_short_circuits_act_on(): def test_identity_commutes(): - assert cirq.I._commutes_(cirq.X) - assert cirq.I._commutes_("placeholder") is NotImplemented + assert cirq.commutes(cirq.I, cirq.X) + with pytest.raises(TypeError): + cirq.commutes(cirq.I, "Gate") From cd4912f6d72c4590538dc202cc28a91eba7fafcd Mon Sep 17 00:00:00 2001 From: "Michael A. Perlin" Date: Sun, 18 Aug 2024 11:53:37 -0500 Subject: [PATCH 11/14] type fix --- cirq-core/cirq/ops/identity.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cirq-core/cirq/ops/identity.py b/cirq-core/cirq/ops/identity.py index 9df805be5e6..a910e703726 100644 --- a/cirq-core/cirq/ops/identity.py +++ b/cirq-core/cirq/ops/identity.py @@ -20,6 +20,7 @@ from cirq import protocols, value from cirq._doc import document +from cirq.type_workarounds import NotImplementedType from cirq.ops import raw_types if TYPE_CHECKING: @@ -75,7 +76,7 @@ def __pow__(self, power: Any) -> Any: return self return NotImplemented - def _commutes_(self, other: Any, *, atol: float = 1e-8) -> bool: + def _commutes_(self, other: Any, *, atol: float = 1e-8) -> Union[bool, NotImplementedType]: """The identity gate commutes with all other gates.""" if not isinstance(other, raw_types.Gate): return NotImplemented From 55b77305246e2a66d3093e389bd5fe8b7da76d2d Mon Sep 17 00:00:00 2001 From: "Michael A. Perlin" Date: Sun, 18 Aug 2024 11:54:17 -0500 Subject: [PATCH 12/14] import fix --- cirq-core/cirq/ops/identity.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cirq-core/cirq/ops/identity.py b/cirq-core/cirq/ops/identity.py index a910e703726..361e70c4ea7 100644 --- a/cirq-core/cirq/ops/identity.py +++ b/cirq-core/cirq/ops/identity.py @@ -13,7 +13,7 @@ # limitations under the License. """IdentityGate.""" -from typing import Any, Dict, Optional, Tuple, TYPE_CHECKING, Sequence +from typing import Any, Dict, Optional, Tuple, TYPE_CHECKING, Sequence, Union import numpy as np import sympy From b5282da02893c52845a66434280d6e7d9cf915d3 Mon Sep 17 00:00:00 2001 From: "Michael A. Perlin" Date: Sun, 18 Aug 2024 12:10:57 -0500 Subject: [PATCH 13/14] minor test change --- cirq-core/cirq/ops/identity_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cirq-core/cirq/ops/identity_test.py b/cirq-core/cirq/ops/identity_test.py index 11a2a21a024..9fa7d301dac 100644 --- a/cirq-core/cirq/ops/identity_test.py +++ b/cirq-core/cirq/ops/identity_test.py @@ -211,6 +211,6 @@ def test_identity_short_circuits_act_on(): def test_identity_commutes(): - assert cirq.commutes(cirq.I, cirq.X) + assert cirq.I._commutes_(cirq.X) with pytest.raises(TypeError): cirq.commutes(cirq.I, "Gate") From 23a74807d2dad8498335c4aa69377073d6396e2b Mon Sep 17 00:00:00 2001 From: "Michael A. Perlin" Date: Sun, 18 Aug 2024 14:44:27 -0500 Subject: [PATCH 14/14] Revert "minor test change" This reverts commit b5282da02893c52845a66434280d6e7d9cf915d3. --- cirq-core/cirq/ops/identity_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cirq-core/cirq/ops/identity_test.py b/cirq-core/cirq/ops/identity_test.py index 9fa7d301dac..11a2a21a024 100644 --- a/cirq-core/cirq/ops/identity_test.py +++ b/cirq-core/cirq/ops/identity_test.py @@ -211,6 +211,6 @@ def test_identity_short_circuits_act_on(): def test_identity_commutes(): - assert cirq.I._commutes_(cirq.X) + assert cirq.commutes(cirq.I, cirq.X) with pytest.raises(TypeError): cirq.commutes(cirq.I, "Gate")