Skip to content

Commit

Permalink
Add to_phased_xz_gate function for SingleQubitCliffordGate (#4050)
Browse files Browse the repository at this point in the history
Allow transferring SingleQubitCliffordGate to Phased X and Z Gates.
  • Loading branch information
bichengying committed May 19, 2021
1 parent 38ed9ac commit 3e06fdf
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 1 deletion.
58 changes: 57 additions & 1 deletion cirq-core/cirq/ops/clifford_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

from cirq import protocols, value, linalg
from cirq._doc import document
from cirq.ops import common_gates, gate_features, named_qubit, pauli_gates
from cirq.ops import common_gates, gate_features, named_qubit, pauli_gates, phased_x_z_gate
from cirq.ops.pauli_gates import Pauli
from cirq.type_workarounds import NotImplementedType

Expand Down Expand Up @@ -242,6 +242,62 @@ def from_unitary(u: np.ndarray) -> Optional['SingleQubitCliffordGate']:
def transform(self, pauli: Pauli) -> PauliTransform:
return self._rotation_map[pauli]

def to_phased_xz_gate(self) -> phased_x_z_gate.PhasedXZGate:
"""Convert this gate to a PhasedXZGate instance.
The rotation can be categorized by {axis} * {degree}:
* Identity: I
* {x, y, z} * {90, 180, 270} --- {X, Y, Z} + 6 Quarter turn gates
* {+/-xy, +/-yz, +/-zx} * 180 --- 6 Hadamard-like gates
* {middle point of xyz in 4 Quadrant} * {120, 240} --- swapping axis
note 1 + 9 + 6 + 8 = 24 in total.
To associate with Clifford Tableau, it can also be grouped by 4:
* {I,X,Y,Z} is [[1 0], [0, 1]]
* {+/- X_sqrt, 2 Hadamard-like gates acting on the YZ plane} is [[1, 0], [1, 1]]
* {+/- Z_sqrt, 2 Hadamard-like gates acting on the XY plane} is [[1, 1], [0, 1]]
* {+/- Y_sqrt, 2 Hadamard-like gates acting on the XZ plane} is [[0, 1], [1, 0]]
* {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]]
"""
x_to = self._rotation_map[pauli_gates.X]
z_to = self._rotation_map[pauli_gates.Z]
flip_index = int(z_to.flip) * 2 + int(x_to.flip)
a, x, z = 0.0, 0.0, 0.0

if (x_to.to, z_to.to) == (pauli_gates.X, pauli_gates.Z):
# I, Z, X, Y cases
to_phased_xz = [(0.0, 0.0, 0.0), (0.0, 0.0, 1.0), (0.0, 1.0, 0.0), (0.5, 1.0, 0.0)]
a, x, z = to_phased_xz[flip_index]
elif (x_to.to, z_to.to) == (pauli_gates.X, pauli_gates.Y):
# +/- X_sqrt, 2 Hadamard-like gates acting on the YZ plane
a = 0.0
x = 0.5 if x_to.flip ^ z_to.flip else -0.5
z = 1.0 if x_to.flip else 0.0
elif (x_to.to, z_to.to) == (pauli_gates.Z, pauli_gates.X):
# +/- Y_sqrt, 2 Hadamard-like gates acting on the XZ plane
a = 0.5
x = 0.5 if x_to.flip else -0.5
z = 0.0 if x_to.flip ^ z_to.flip else 1.0
elif (x_to.to, z_to.to) == (pauli_gates.Y, pauli_gates.Z):
# +/- Z_sqrt, 2 Hadamard-like gates acting on the XY plane
to_phased_xz = [(0.0, 0.0, 0.5), (0.0, 0.0, -0.5), (0.25, 1.0, 0.0), (-0.25, 1.0, 0.0)]
a, x, z = to_phased_xz[flip_index]
elif (x_to.to, z_to.to) == (pauli_gates.Z, pauli_gates.Y):
# axis swapping rotation -- (312) permutation
a = 0.5
x = 0.5 if x_to.flip else -0.5
z = 0.5 if x_to.flip ^ z_to.flip else -0.5
else:
# axis swapping rotation -- (231) permutation.
# This should be the only cases left.
assert (x_to.to, z_to.to) == (pauli_gates.Y, pauli_gates.X)
a = 0.0
x = -0.5 if x_to.flip ^ z_to.flip else 0.5
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 _value_equality_values_(self):
return (
self.transform(pauli_gates.X),
Expand Down
13 changes: 13 additions & 0 deletions cirq-core/cirq/ops/clifford_gate_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -517,3 +517,16 @@ def test_from_unitary_not_clifford():
# Not a Clifford gate.
u = cirq.unitary(cirq.T)
assert cirq.SingleQubitCliffordGate.from_unitary(u) is None


@pytest.mark.parametrize('trans_x,trans_z', _all_rotation_pairs())
def test_to_phased_xz_gate(trans_x, trans_z):
gate = cirq.SingleQubitCliffordGate.from_xz_map(trans_x, trans_z)
actual_phased_xz_gate = gate.to_phased_xz_gate()._canonical()
expect_phased_xz_gates = cirq.PhasedXZGate.from_matrix(cirq.unitary(gate))

assert np.isclose(actual_phased_xz_gate.x_exponent, expect_phased_xz_gates.x_exponent)
assert np.isclose(actual_phased_xz_gate.z_exponent, expect_phased_xz_gates.z_exponent)
assert np.isclose(
actual_phased_xz_gate.axis_phase_exponent, expect_phased_xz_gates.axis_phase_exponent
)

0 comments on commit 3e06fdf

Please sign in to comment.