Repository: quantumlib/Cirq

Issue:
Override of PhasedXPowGate causes determinant of gate to jump discontinuously
```
>>> np.linalg.det(cirq.unitary(cirq.PhasedXPowGate(phase_exponent=1.01, exponent=0.5)))
0.9999999999999998j
>>> np.linalg.det(cirq.unitary(cirq.PhasedXPowGate(phase_exponent=1., exponent=0.5)))
-0.9999999999999998j
>>> np.linalg.det(cirq.unitary(cirq.PhasedXPowGate(phase_exponent=0.99, exponent=0.5)))
0.9999999999999998j
```
and the gates also change badly
```
>>> cirq.unitary(cirq.PhasedXPowGate(phase_exponent=1.01, exponent=0.5))
array([[ 0.5       +0.5j       , -0.4840479 +0.51545866j],
       [-0.51545866+0.4840479j ,  0.5       +0.5j       ]])
>>> cirq.unitary(cirq.PhasedXPowGate(phase_exponent=1, exponent=0.5))
array([[0.5-0.5j, 0.5+0.5j],
       [0.5+0.5j, 0.5-0.5j]])
>>> cirq.unitary(cirq.PhasedXPowGate(phase_exponent=0.99, exponent=0.5))
array([[ 0.5       +0.5j       , -0.51545866+0.4840479j ],
       [-0.4840479 +0.51545866j,  0.5       +0.5j       ]])
```

These discontinuous jumps are likely coming from the __new__ override code.



Discussion:

Resolving patch:
diff --git a/cirq/ops/phased_x_gate.py b/cirq/ops/phased_x_gate.py
--- a/cirq/ops/phased_x_gate.py
+++ b/cirq/ops/phased_x_gate.py
@@ -48,17 +48,8 @@ def __new__(cls,
                 exponent=exponent,
                 global_shift=global_shift)
         if p == 0.5:
-            return cirq.ops.common_gates.YPowGate(
-                exponent=exponent,
-                global_shift=global_shift)
-        if p == 1 and not isinstance(exponent, sympy.Symbol):
-            return cirq.ops.common_gates.XPowGate(
-                exponent=-exponent,
-                global_shift=global_shift)
-        if p == -0.5 and not isinstance(exponent, sympy.Symbol):
-            return cirq.ops.common_gates.YPowGate(
-                exponent=-exponent,
-                global_shift=global_shift)
+            return cirq.ops.common_gates.YPowGate(exponent=exponent,
+                                                  global_shift=global_shift)
         return super().__new__(cls)
 
     def __init__(self,


Test patch:
diff --git a/cirq/google/convert_to_xmon_gates_test.py b/cirq/google/convert_to_xmon_gates_test.py
--- a/cirq/google/convert_to_xmon_gates_test.py
+++ b/cirq/google/convert_to_xmon_gates_test.py
@@ -34,4 +34,5 @@ def test_avoids_infinite_cycle_when_matrix_available():
     q = cirq.GridQubit(0, 0)
     c = cirq.Circuit.from_ops(OtherX().on(q), OtherOtherX().on(q))
     cirq.google.ConvertToXmonGates().optimize_circuit(c)
-    cirq.testing.assert_has_diagram(c, '(0, 0): ───X───X───')
+    cirq.testing.assert_has_diagram(
+        c, '(0, 0): ───PhasedX(1.0)───PhasedX(1.0)───')
diff --git a/cirq/ion/convert_to_ion_gates_test.py b/cirq/ion/convert_to_ion_gates_test.py
--- a/cirq/ion/convert_to_ion_gates_test.py
+++ b/cirq/ion/convert_to_ion_gates_test.py
@@ -55,21 +55,27 @@ def test_convert_to_ion_gates():
     rx = cirq.ion.ConvertToIonGates().convert_one(OtherX().on(q0))
     rop = cirq.ion.ConvertToIonGates().convert_one(op)
     rcnot = cirq.ion.ConvertToIonGates().convert_one(OtherCNOT().on(q0, q1))
-    assert rx == [(cirq.X**1.0).on(cirq.GridQubit(0, 0))]
+    assert rx == [
+        cirq.PhasedXPowGate(phase_exponent=1).on(cirq.GridQubit(0, 0))
+    ]
     assert rop == [cirq.Ry(np.pi/2).on(op.qubits[0]),
                    cirq.ion.MS(np.pi/4).on(op.qubits[0], op.qubits[1]),
                    cirq.ops.Rx(-1*np.pi/2).on(op.qubits[0]),
                    cirq.ops.Rx(-1*np.pi/2).on(op.qubits[1]),
                    cirq.ops.Ry(-1*np.pi/2).on(op.qubits[0])]
-    assert rcnot == [cirq.PhasedXPowGate(phase_exponent=-0.75, exponent=0.5).
-                         on(cirq.GridQubit(0, 0)),
-                     (cirq.X**-0.25).on(cirq.GridQubit(0, 1)),
-                     cirq.T.on(cirq.GridQubit(0, 0)),
-                     cirq.MS(-0.5*np.pi/2).on(cirq.GridQubit(0, 0),
-                                              cirq.GridQubit(0, 1)),
-                     (cirq.Y**0.5).on(cirq.GridQubit(0, 0)),
-                     (cirq.X**-0.25).on(cirq.GridQubit(0, 1)),
-                     (cirq.Z**-0.75).on(cirq.GridQubit(0, 0))]
+    assert rcnot == [
+        cirq.PhasedXPowGate(phase_exponent=-0.75,
+                            exponent=0.5).on(cirq.GridQubit(0, 0)),
+        cirq.PhasedXPowGate(phase_exponent=1,
+                            exponent=0.25).on(cirq.GridQubit(0, 1)),
+        cirq.T.on(cirq.GridQubit(0, 0)),
+        cirq.MS(-0.5 * np.pi / 2).on(cirq.GridQubit(0, 0), cirq.GridQubit(0,
+                                                                          1)),
+        (cirq.Y**0.5).on(cirq.GridQubit(0, 0)),
+        cirq.PhasedXPowGate(phase_exponent=1,
+                            exponent=0.25).on(cirq.GridQubit(0, 1)),
+        (cirq.Z**-0.75).on(cirq.GridQubit(0, 0))
+    ]
 
 
 def test_convert_to_ion_circuit():
diff --git a/cirq/neutral_atoms/convert_to_neutral_atom_gates_test.py b/cirq/neutral_atoms/convert_to_neutral_atom_gates_test.py
--- a/cirq/neutral_atoms/convert_to_neutral_atom_gates_test.py
+++ b/cirq/neutral_atoms/convert_to_neutral_atom_gates_test.py
@@ -59,7 +59,8 @@ def _decompose_(self, qubits):
     q = cirq.GridQubit(0, 0)
     c = cirq.Circuit.from_ops(OtherX().on(q), OtherOtherX().on(q))
     cirq.neutral_atoms.ConvertToNeutralAtomGates().optimize_circuit(c)
-    cirq.testing.assert_has_diagram(c, '(0, 0): ───X───X───')
+    cirq.testing.assert_has_diagram(
+        c, '(0, 0): ───PhasedX(1.0)───PhasedX(1.0)───')
 
 
 def test_avoids_decompose_fallback_when_matrix_available_two_qubit():
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
@@ -703,12 +703,8 @@ def test_rz_unitary():
 
 def test_phase_by_xy():
     assert cirq.phase_by(cirq.X, 0.25, 0) == cirq.Y
-    assert cirq.phase_by(cirq.Y, 0.25, 0) == cirq.X
-
     assert cirq.phase_by(cirq.X**0.5, 0.25, 0) == cirq.Y**0.5
-    assert cirq.phase_by(cirq.Y**0.5, 0.25, 0) == cirq.X**-0.5
     assert cirq.phase_by(cirq.X**-0.5, 0.25, 0) == cirq.Y**-0.5
-    assert cirq.phase_by(cirq.Y**-0.5, 0.25, 0) == cirq.X**0.5
 
 
 def test_ixyz_circuit_diagram():
diff --git a/cirq/ops/phased_x_gate_test.py b/cirq/ops/phased_x_gate_test.py
--- a/cirq/ops/phased_x_gate_test.py
+++ b/cirq/ops/phased_x_gate_test.py
@@ -49,9 +49,7 @@ def test_new_init():
     assert g._global_shift == 0.1
 
     assert isinstance(cirq.PhasedXPowGate(phase_exponent=0), cirq.XPowGate)
-    assert isinstance(cirq.PhasedXPowGate(phase_exponent=1), cirq.XPowGate)
     assert isinstance(cirq.PhasedXPowGate(phase_exponent=0.5), cirq.YPowGate)
-    assert isinstance(cirq.PhasedXPowGate(phase_exponent=1.5), cirq.YPowGate)
 
     x = cirq.PhasedXPowGate(phase_exponent=0,
                             exponent=0.1,
@@ -91,10 +89,8 @@ def test_extrapolate():
 def test_eq():
     eq = cirq.testing.EqualsTester()
     eq.add_equality_group(cirq.PhasedXPowGate(phase_exponent=0),
-                          cirq.PhasedXPowGate(phase_exponent=0,
-                                              exponent=1),
+                          cirq.PhasedXPowGate(phase_exponent=0, exponent=1),
                           cirq.PhasedXPowGate(exponent=1, phase_exponent=0),
-                          cirq.PhasedXPowGate(exponent=1, phase_exponent=1),
                           cirq.PhasedXPowGate(exponent=1, phase_exponent=2),
                           cirq.PhasedXPowGate(exponent=1, phase_exponent=-2),
                           cirq.X)
@@ -102,13 +98,11 @@ def test_eq():
                                               phase_exponent=2,
                                               global_shift=0.1))
 
-    eq.add_equality_group(cirq.PhasedXPowGate(phase_exponent=0.5,
-                                              exponent=1),
-                          cirq.PhasedXPowGate(phase_exponent=2.5,
-                                              exponent=3),
-                          cirq.Y,
-                          cirq.PhasedXPowGate(phase_exponent=-0.5,
-                                              exponent=1))
+    eq.add_equality_group(
+        cirq.PhasedXPowGate(phase_exponent=0.5, exponent=1),
+        cirq.PhasedXPowGate(phase_exponent=2.5, exponent=3),
+        cirq.Y,
+    )
     eq.add_equality_group(cirq.PhasedXPowGate(phase_exponent=0.5,
                                               exponent=0.25),
                           cirq.Y**0.25)
diff --git a/cirq/optimizers/decompositions_test.py b/cirq/optimizers/decompositions_test.py
--- a/cirq/optimizers/decompositions_test.py
+++ b/cirq/optimizers/decompositions_test.py
@@ -175,7 +175,8 @@ def test_single_qubit_op_to_framed_phase_form_equivalent_on_known_and_random(
 def test_single_qubit_matrix_to_native_gates_known():
     actual = cirq.single_qubit_matrix_to_phased_x_z(
         np.array([[0, 1], [1, 0]]), atol=0.01)
-    assert cirq.approx_eq(actual, [cirq.X], atol=1e-9)
+    assert cirq.approx_eq(actual, [cirq.PhasedXPowGate(phase_exponent=1.0)],
+                          atol=1e-9)
 
     actual = cirq.single_qubit_matrix_to_phased_x_z(
         np.array([[0, -1j], [1j, 0]]), atol=0.01)
@@ -195,7 +196,10 @@ def test_single_qubit_matrix_to_native_gates_known():
 
     actual = cirq.single_qubit_matrix_to_phased_x_z(
         np.array([[1, 1], [1, -1]]) * np.sqrt(0.5), atol=0.001)
-    assert cirq.approx_eq(actual, [cirq.Y**-0.5, cirq.Z], atol=1e-9)
+    assert cirq.approx_eq(
+        actual,
+        [cirq.PhasedXPowGate(phase_exponent=-0.5, exponent=0.5), cirq.Z**-1],
+        atol=1e-9)
 
 
 @pytest.mark.parametrize('intended_effect', [
diff --git a/cirq/optimizers/eject_z_test.py b/cirq/optimizers/eject_z_test.py
--- a/cirq/optimizers/eject_z_test.py
+++ b/cirq/optimizers/eject_z_test.py
@@ -176,7 +176,7 @@ class UnknownGate(cirq.SingleQubitGate):
             cirq.Moment([cirq.Z(q)]),
             cirq.Moment([u(q)]),
             cirq.Moment(),
-            cirq.Moment([cirq.Y(q)**-1.0]),
+            cirq.Moment([cirq.PhasedXPowGate(phase_exponent=-0.5)(q)]),
             cirq.Moment(),
             cirq.Moment([cirq.PhasedXPowGate(phase_exponent=-0.75).on(q)]),
             cirq.Moment([cirq.Z(q)**0.75]),
diff --git a/cirq/optimizers/merge_single_qubit_gates_test.py b/cirq/optimizers/merge_single_qubit_gates_test.py
--- a/cirq/optimizers/merge_single_qubit_gates_test.py
+++ b/cirq/optimizers/merge_single_qubit_gates_test.py
@@ -39,6 +39,7 @@ def assert_optimizes(
     assert before == expected, 'BEFORE {} : EXPECTED {}'.format(before,
                                                                 expected)
 
+
 def test_leaves_singleton():
     m = cirq.MergeSingleQubitGates()
     q = cirq.NamedQubit('q')
@@ -170,18 +171,17 @@ def test_rewrite():
 
 def test_merge_single_qubit_gates_into_phased_x_z():
     a, b = cirq.LineQubit.range(2)
-    assert_optimizes(
-        before=cirq.Circuit.from_ops(
-            cirq.X(a),
-            cirq.Y(b)**0.5,
-            cirq.CZ(a, b),
-            cirq.H(a),
-            cirq.Z(a),
-        ),
-        expected=cirq.Circuit.from_ops(
-            cirq.X(a),
-            cirq.Y(b)**0.5,
-            cirq.CZ(a, b),
-            cirq.Y(a)**-0.5,
-        ),
-        optimizer=cirq.merge_single_qubit_gates_into_phased_x_z)
+    assert_optimizes(before=cirq.Circuit.from_ops(
+        cirq.X(a),
+        cirq.Y(b)**0.5,
+        cirq.CZ(a, b),
+        cirq.H(a),
+        cirq.Z(a),
+    ),
+                     expected=cirq.Circuit.from_ops(
+                         cirq.PhasedXPowGate(phase_exponent=1)(a),
+                         cirq.Y(b)**0.5,
+                         cirq.CZ(a, b),
+                         (cirq.PhasedXPowGate(phase_exponent=-0.5)(a))**0.5,
+                     ),
+                     optimizer=cirq.merge_single_qubit_gates_into_phased_x_z)