Skip to content

Commit

Permalink
Increase coverage of clifford protocols to parity_gates (#6338)
Browse files Browse the repository at this point in the history
  • Loading branch information
NoureldinYosri authored Nov 11, 2023
1 parent 11ae0bd commit 0b90ed6
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 18 deletions.
80 changes: 62 additions & 18 deletions cirq-core/cirq/ops/parity_gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,23 @@

"""Quantum gates that phase with respect to product-of-pauli observables."""

from typing import Any, Dict, List, Optional, Tuple, Union, TYPE_CHECKING
from typing import Any, Dict, List, Optional, Tuple, Union, TYPE_CHECKING, Sequence
from typing_extensions import Self

import numpy as np

from cirq import protocols, value
from cirq._compat import proper_repr
from cirq._doc import document
from cirq.ops import gate_features, eigen_gate, common_gates, pauli_gates
from cirq.ops import (
gate_features,
eigen_gate,
common_gates,
pauli_gates,
clifford_gate,
pauli_interaction_gate,
)


if TYPE_CHECKING:
import cirq
Expand Down Expand Up @@ -87,25 +95,29 @@ def _trace_distance_bound_(self) -> Optional[float]:
return abs(np.sin(self._exponent * 0.5 * np.pi))

def _decompose_into_clifford_with_qubits_(self, qubits):
from cirq.ops.clifford_gate import SingleQubitCliffordGate
from cirq.ops.pauli_interaction_gate import PauliInteractionGate

if self.exponent % 2 == 0:
return []
if self.exponent % 2 == 0.5:
return [
PauliInteractionGate(pauli_gates.X, False, pauli_gates.X, False).on(*qubits),
SingleQubitCliffordGate.X_sqrt.on_each(*qubits),
pauli_interaction_gate.PauliInteractionGate(
pauli_gates.X, False, pauli_gates.X, False
).on(*qubits),
clifford_gate.SingleQubitCliffordGate.X_sqrt.on_each(*qubits),
]
if self.exponent % 2 == 1:
return [SingleQubitCliffordGate.X.on_each(*qubits)]
return [clifford_gate.SingleQubitCliffordGate.X.on_each(*qubits)]
if self.exponent % 2 == 1.5:
return [
PauliInteractionGate(pauli_gates.X, False, pauli_gates.X, False).on(*qubits),
SingleQubitCliffordGate.X_nsqrt.on_each(*qubits),
pauli_interaction_gate.PauliInteractionGate(
pauli_gates.X, False, pauli_gates.X, False
).on(*qubits),
clifford_gate.SingleQubitCliffordGate.X_nsqrt.on_each(*qubits),
]
return NotImplemented

def _has_stabilizer_effect_(self) -> bool:
return self.exponent % 2 in (0, 0.5, 1, 1.5)

def _decompose_(self, qubits: Tuple['cirq.Qid', ...]) -> 'cirq.OP_TREE':
yield common_gates.YPowGate(exponent=-0.5).on_each(*qubits)
yield ZZPowGate(exponent=self.exponent, global_shift=self.global_shift)(*qubits)
Expand Down Expand Up @@ -192,25 +204,29 @@ def _trace_distance_bound_(self) -> Optional[float]:
return abs(np.sin(self._exponent * 0.5 * np.pi))

def _decompose_into_clifford_with_qubits_(self, qubits):
from cirq.ops.clifford_gate import SingleQubitCliffordGate
from cirq.ops.pauli_interaction_gate import PauliInteractionGate

if self.exponent % 2 == 0:
return []
if self.exponent % 2 == 0.5:
return [
PauliInteractionGate(pauli_gates.Y, False, pauli_gates.Y, False).on(*qubits),
SingleQubitCliffordGate.Y_sqrt.on_each(*qubits),
pauli_interaction_gate.PauliInteractionGate(
pauli_gates.Y, False, pauli_gates.Y, False
).on(*qubits),
clifford_gate.SingleQubitCliffordGate.Y_sqrt.on_each(*qubits),
]
if self.exponent % 2 == 1:
return [SingleQubitCliffordGate.Y.on_each(*qubits)]
return [clifford_gate.SingleQubitCliffordGate.Y.on_each(*qubits)]
if self.exponent % 2 == 1.5:
return [
PauliInteractionGate(pauli_gates.Y, False, pauli_gates.Y, False).on(*qubits),
SingleQubitCliffordGate.Y_nsqrt.on_each(*qubits),
pauli_interaction_gate.PauliInteractionGate(
pauli_gates.Y, False, pauli_gates.Y, False
).on(*qubits),
clifford_gate.SingleQubitCliffordGate.Y_nsqrt.on_each(*qubits),
]
return NotImplemented

def _has_stabilizer_effect_(self) -> bool:
return self.exponent % 2 in (0, 0.5, 1, 1.5)

def _decompose_(self, qubits: Tuple['cirq.Qid', ...]) -> 'cirq.OP_TREE':
yield common_gates.XPowGate(exponent=0.5).on_each(*qubits)
yield ZZPowGate(exponent=self.exponent, global_shift=self.global_shift)(*qubits)
Expand Down Expand Up @@ -265,6 +281,34 @@ def _decompose_(self, qubits):
exponent=-2 * self.exponent, global_shift=-self.global_shift / 2
)(qubits[0], qubits[1])

def _decompose_into_clifford_with_qubits_(
self, qubits: Sequence['cirq.Qid']
) -> Sequence[Union['cirq.Operation', Sequence['cirq.Operation']]]:
if not self._has_stabilizer_effect_():
return NotImplemented
if self.exponent % 2 == 0:
return []
if self.exponent % 2 == 1:
return clifford_gate.SingleQubitCliffordGate.Z.on_each(*qubits)

if self.exponent % 2 == 0.5:
return [
pauli_interaction_gate.PauliInteractionGate(
pauli_gates.Z, False, pauli_gates.Z, False
).on(*qubits),
clifford_gate.SingleQubitCliffordGate.Z_sqrt.on_each(*qubits),
]
else:
return [
pauli_interaction_gate.PauliInteractionGate(
pauli_gates.Z, False, pauli_gates.Z, False
).on(*qubits),
clifford_gate.SingleQubitCliffordGate.Z_nsqrt.on_each(*qubits),
]

def _has_stabilizer_effect_(self) -> bool:
return self.exponent % 2 in (0, 0.5, 1, 1.5)

def _eigen_components(self) -> List[Tuple[float, np.ndarray]]:
return [(0, np.diag([1, 0, 0, 1])), (1, np.diag([0, 1, 1, 0]))]

Expand Down
23 changes: 23 additions & 0 deletions cirq-core/cirq/ops/parity_gates_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,3 +332,26 @@ def custom_resolver(cirq_type: str):
json_text=cirq.to_json(cirq.ms(np.pi / 2)), resolvers=[custom_resolver]
) == cirq.ms(np.pi / 2)
assert custom_resolver('X') is None


@pytest.mark.parametrize('gate_cls', (cirq.XXPowGate, cirq.YYPowGate, cirq.ZZPowGate))
@pytest.mark.parametrize(
'exponent,is_clifford',
((0, True), (0.5, True), (0.75, False), (1, True), (1.5, True), (-1.5, True)),
)
def test_clifford_protocols(gate_cls: type[cirq.EigenGate], exponent: float, is_clifford: bool):
gate = gate_cls(exponent=exponent)
assert hasattr(gate, '_decompose_into_clifford_with_qubits_')
if is_clifford:
clifford_decomposition = cirq.Circuit(
gate._decompose_into_clifford_with_qubits_(cirq.LineQubit.range(2))
)
assert cirq.has_stabilizer_effect(gate)
assert cirq.has_stabilizer_effect(clifford_decomposition)
if exponent == 0:
assert clifford_decomposition == cirq.Circuit()
else:
np.testing.assert_allclose(cirq.unitary(gate), cirq.unitary(clifford_decomposition))
else:
assert not cirq.has_stabilizer_effect(gate)
assert gate._decompose_into_clifford_with_qubits_(cirq.LineQubit.range(2)) is NotImplemented

0 comments on commit 0b90ed6

Please sign in to comment.