Repository: quantumlib/Cirq

Issue:
Transformation that turns Pauli**exponent gates to Rp(theta) gates and vice versa
These are the same up to global phase, but different people like different version of this gate.


Discussion:

Resolving patch:
diff --git a/cirq/ops/common_gates.py b/cirq/ops/common_gates.py
--- a/cirq/ops/common_gates.py
+++ b/cirq/ops/common_gates.py
@@ -81,6 +81,14 @@ def _apply_unitary_(self, args: protocols.ApplyUnitaryArgs
             args.available_buffer *= p
         return args.available_buffer
 
+    def in_su2(self) -> 'XPowGate':
+        """Returns an equal-up-global-phase gate from the group SU2."""
+        return XPowGate(exponent=self._exponent, global_shift=-0.5)
+
+    def with_canonical_global_phase(self) -> 'XPowGate':
+        """Returns an equal-up-global-phase standardized form of the gate."""
+        return XPowGate(exponent=self._exponent)
+
     def _eigen_components(self):
         return [
             (0, np.array([[0.5, 0.5], [0.5, 0.5]])),
@@ -184,6 +192,14 @@ class YPowGate(eigen_gate.EigenGate,
     `cirq.Y`, the Pauli Y gate, is an instance of this gate at exponent=1.
     """
 
+    def in_su2(self) -> 'YPowGate':
+        """Returns an equal-up-global-phase gate from the group SU2."""
+        return YPowGate(exponent=self._exponent, global_shift=-0.5)
+
+    def with_canonical_global_phase(self) -> 'YPowGate':
+        """Returns an equal-up-global-phase standardized form of the gate."""
+        return YPowGate(exponent=self._exponent)
+
     def _eigen_components(self):
         return [
             (0, np.array([[0.5, -0.5j], [0.5j, 0.5]])),
@@ -298,6 +314,14 @@ def _apply_unitary_(self, args: protocols.ApplyUnitaryArgs
             args.target_tensor *= p
         return args.target_tensor
 
+    def in_su2(self) -> 'ZPowGate':
+        """Returns an equal-up-global-phase gate from the group SU2."""
+        return ZPowGate(exponent=self._exponent, global_shift=-0.5)
+
+    def with_canonical_global_phase(self) -> 'ZPowGate':
+        """Returns an equal-up-global-phase standardized form of the gate."""
+        return ZPowGate(exponent=self._exponent)
+
     def _eigen_components(self):
         return [
             (0, np.diag([1, 0])),


Test patch:
diff --git a/cirq/ops/common_gates_test.py b/cirq/ops/common_gates_test.py
--- a/cirq/ops/common_gates_test.py
+++ b/cirq/ops/common_gates_test.py
@@ -67,6 +67,28 @@ def test_cz_init():
     assert (cirq.CZ**0.5).exponent == 0.5
 
 
+def test_transformations():
+    pi = np.pi
+    initialRx = cirq.Rx(0.4)
+    expectedPowx = cirq.X**(0.4 / pi)
+    receivedPowx = initialRx.with_canonical_global_phase()
+    backToRx = receivedPowx.in_su2()
+    assert receivedPowx == expectedPowx
+    assert backToRx == initialRx
+    initialRy = cirq.Ry(0.123)
+    expectedPowy = cirq.Y**(0.123 / pi)
+    receivedPowy = initialRy.with_canonical_global_phase()
+    backToRy = receivedPowy.in_su2()
+    assert receivedPowy == expectedPowy
+    assert backToRy == initialRy
+    initialRz = cirq.Rz(-1.53)
+    expectedPowz = cirq.Z**(-1.53 / pi)
+    receivedPowz = initialRz.with_canonical_global_phase()
+    backToRz = receivedPowz.in_su2()
+    assert receivedPowz == expectedPowz
+    assert backToRz == initialRz
+
+
 def test_cz_str():
     assert str(cirq.CZ) == 'CZ'
     assert str(cirq.CZ**0.5) == 'CZ**0.5'