Repository: quantumlib/Cirq

Issue:
Move PauliString out of contrib and add `operations_to_convert_to_computational_basis`
Will be useful in https://github.com/quantumlib/Cirq/pull/507


Discussion:
Where should it go?  cirq.ops?
Yeah, that's a reasonable place to put it.
Note that this is not on the milestone; not urgent.

Resolving patch:
diff --git a/cirq/contrib/paulistring/pauli_string_phasor.py b/cirq/contrib/paulistring/pauli_string_phasor.py
--- a/cirq/contrib/paulistring/pauli_string_phasor.py
+++ b/cirq/contrib/paulistring/pauli_string_phasor.py
@@ -18,8 +18,6 @@
 
 from cirq import ops, value, study, extension
 
-from cirq.ops.pauli import Pauli
-from cirq.ops.clifford_gate import CliffordGate
 from cirq.ops.pauli_string import PauliString
 from cirq.contrib.paulistring.pauli_string_raw_types import (
     PauliStringGateOperation)
@@ -112,7 +110,7 @@ def default_decompose(self) -> ops.OP_TREE:
             return
         qubits = self.qubits
         any_qubit = qubits[0]
-        to_z_ops = tuple(pauli_string_to_z_ops(self.pauli_string))
+        to_z_ops = ops.freeze_op_tree(self.pauli_string.to_z_basis_ops())
         xor_decomp = tuple(xor_nonlocal_decompose(qubits, any_qubit))
         yield to_z_ops
         yield xor_decomp
@@ -164,14 +162,6 @@ def __str__(self):
         return '{}**{}'.format(self.pauli_string, self.half_turns)
 
 
-def pauli_string_to_z_ops(pauli_string: PauliString) -> Iterable[ops.Operation]:
-    """Yields the single qubit operations to apply before a Pauli string of Zs
-    (and apply the inverse of these operations after) to make it equivalent to
-    the given pauli_string."""
-    for qubit, pauli in pauli_string.items():
-        yield CliffordGate.from_single_map({pauli: (Pauli.Z, False)})(qubit)
-
-
 def xor_nonlocal_decompose(qubits: Iterable[ops.QubitId],
                            onto_qubit: ops.QubitId) -> Iterable[ops.Operation]:
     """Decomposition ignores connectivity."""
diff --git a/cirq/ops/pauli_string.py b/cirq/ops/pauli_string.py
--- a/cirq/ops/pauli_string.py
+++ b/cirq/ops/pauli_string.py
@@ -15,7 +15,7 @@
 from typing import (Any, Dict, ItemsView, Iterable, Iterator, KeysView, Mapping,
                     Optional, Tuple, ValuesView)
 
-from cirq.ops import raw_types, gate_operation, qubit_order
+from cirq.ops import raw_types, gate_operation, qubit_order, op_tree
 from cirq.ops.pauli import Pauli
 from cirq.ops.clifford_gate import CliffordGate
 from cirq.ops.pauli_interaction_gate import PauliInteractionGate
@@ -119,6 +119,12 @@ def map_qubits(self, qubit_map: Dict[raw_types.QubitId, raw_types.QubitId]
                                for qubit, pauli in self.items()}
         return PauliString(new_qubit_pauli_map, self.negated)
 
+    def to_z_basis_ops(self) -> op_tree.OP_TREE:
+        """Returns operations to convert the qubits to the computational basis.
+        """
+        for qubit, pauli in self.items():
+            yield CliffordGate.from_single_map({pauli: (Pauli.Z, False)})(qubit)
+
     def pass_operations_over(self,
                              ops: Iterable[raw_types.Operation],
                              after_to_before: bool = False) -> 'PauliString':


Test patch:
diff --git a/cirq/ops/pauli_string_test.py b/cirq/ops/pauli_string_test.py
--- a/cirq/ops/pauli_string_test.py
+++ b/cirq/ops/pauli_string_test.py
@@ -13,6 +13,7 @@
 # limitations under the License.
 
 import itertools
+import numpy as np
 import pytest
 from cirq.testing import (
     EqualsTester,
@@ -272,6 +273,31 @@ def test_map_qubits():
     assert ps1.map_qubits(qubit_map) == ps2
 
 
+def test_to_z_basis_ops():
+    x0 = np.array([1,1]) / np.sqrt(2)
+    x1 = np.array([1,-1]) / np.sqrt(2)
+    y0 = np.array([1,1j]) / np.sqrt(2)
+    y1 = np.array([1,-1j]) / np.sqrt(2)
+    z0 = np.array([1,0])
+    z1 = np.array([0,1])
+
+    q0, q1, q2, q3, q4, q5 = _make_qubits(6)
+    pauli_string = cirq.PauliString({q0: cirq.Pauli.X, q1: cirq.Pauli.X,
+                                     q2: cirq.Pauli.Y, q3: cirq.Pauli.Y,
+                                     q4: cirq.Pauli.Z, q5: cirq.Pauli.Z})
+    circuit = cirq.Circuit.from_ops(
+                    pauli_string.to_z_basis_ops())
+
+    initial_state = cirq.kron(x0, x1, y0, y1, z0, z1)
+    z_basis_state = circuit.apply_unitary_effect_to_state(initial_state)
+
+    expected_state = np.zeros(2 ** 6)
+    expected_state[0b010101] = 1
+
+    cirq.testing.assert_allclose_up_to_global_phase(
+                    z_basis_state, expected_state, rtol=1e-7, atol=1e-7)
+
+
 def _assert_pass_over(ops, before, after):
     assert before.pass_operations_over(ops) == after
     assert (after.pass_operations_over(ops, after_to_before=True)