Repository: quantumlib/Cirq

Issue:
Need a better compilation for swap_to_sqrt_iswap
Right now it uses 4 sqrtiswaps to support arbitrary angle. For a plain ol' `SWAP` (common for routing qubits around) you should be able to get away with 3

```python
        yield cirq.Y(a) ** 0.5
        yield cirq.Y(b) ** 0.5
        yield SQRT_ISWAP(a, b)
        yield cirq.Y(a) ** -0.5
        yield cirq.Y(b) ** -0.5
        yield SQRT_ISWAP(a, b)
        yield cirq.X(a) ** -0.5
        yield cirq.X(b) ** -0.5
        yield SQRT_ISWAP(a, b)
        yield cirq.X(a) ** 0.5
        yield cirq.X(b) ** 0.5
```

should do it.

cc @dstrain115 @john6060 @Strilanc 


Discussion:

Resolving patch:
diff --git a/cirq/google/optimizers/convert_to_sqrt_iswap.py b/cirq/google/optimizers/convert_to_sqrt_iswap.py
--- a/cirq/google/optimizers/convert_to_sqrt_iswap.py
+++ b/cirq/google/optimizers/convert_to_sqrt_iswap.py
@@ -262,6 +262,21 @@ def swap_to_sqrt_iswap(a, b, turns):
             theta: The rotational angle that specifies the gate, where
             c = cos(π·t/2), s = sin(π·t/2), g = exp(i·π·t/2).
     """
+    if not isinstance(turns, sympy.Basic) and _near_mod_n(turns, 1.0, 2):
+        # Decomposition for cirq.SWAP
+        yield ops.Y(a)**0.5
+        yield ops.Y(b)**0.5
+        yield SQRT_ISWAP(a, b)
+        yield ops.Y(a)**-0.5
+        yield ops.Y(b)**-0.5
+        yield SQRT_ISWAP(a, b)
+        yield ops.X(a)**-0.5
+        yield ops.X(b)**-0.5
+        yield SQRT_ISWAP(a, b)
+        yield ops.X(a)**0.5
+        yield ops.X(b)**0.5
+        return
+
     yield ops.Z(a)**1.25
     yield ops.Z(b)**-0.25
     yield ops.ISWAP(a, b)**-0.5
diff --git a/cirq/ops/moment.py b/cirq/ops/moment.py
--- a/cirq/ops/moment.py
+++ b/cirq/ops/moment.py
@@ -193,6 +193,11 @@ def transform_qubits(self: TSelf_Moment,
     def _json_dict_(self):
         return protocols.obj_to_dict_helper(self, ['operations'])
 
+    def __add__(self, other):
+        if isinstance(other, raw_types.Operation):
+            return self.with_operation(other)
+        return NotImplemented
+
 
 def _list_repr_with_indented_item_lines(items: Sequence[Any]) -> str:
     block = '\n'.join([repr(op) + ',' for op in items])
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -73,4 +73,5 @@
           'cirq': ['py.typed'],
           'cirq.google.api.v1': ['*.proto', '*.pyi'],
           'cirq.google.api.v2': ['*.proto', '*.pyi'],
+          'cirq.protocols.json_test_data': ['*'],
       })


Test patch:
diff --git a/cirq/google/optimizers/convert_to_sqrt_iswap_test.py b/cirq/google/optimizers/convert_to_sqrt_iswap_test.py
--- a/cirq/google/optimizers/convert_to_sqrt_iswap_test.py
+++ b/cirq/google/optimizers/convert_to_sqrt_iswap_test.py
@@ -22,7 +22,7 @@ def _unitaries_allclose(circuit1, circuit2):
     [
         (cast(cirq.Gate, cirq.ISWAP), 7),  # cast is for fixing mypy confusion
         (cirq.CZ, 8),
-        (cirq.SWAP, 13),
+        (cirq.SWAP, 7),
         (cirq.CNOT, 9),
         (cirq.ISWAP**0.5, 1),
         (cirq.ISWAP**-0.5, 1),
diff --git a/cirq/ops/moment_test.py b/cirq/ops/moment_test.py
--- a/cirq/ops/moment_test.py
+++ b/cirq/ops/moment_test.py
@@ -261,3 +261,16 @@ def test_immutable_moment():
         circuit = cirq.Circuit(cirq.X(q1))
         moment = circuit.moments[0]
         moment.operations += (cirq.Y(q2),)
+
+
+def test_add():
+    a, b = cirq.LineQubit.range(2)
+    expected_circuit = cirq.Circuit([cirq.CNOT(a, b), cirq.X(a), cirq.Y(b)])
+
+    circuit1 = cirq.Circuit([cirq.CNOT(a, b), cirq.X(a)])
+    circuit1[1] += cirq.Y(b)
+    assert circuit1 == expected_circuit
+
+    circuit2 = cirq.Circuit(cirq.CNOT(a, b), cirq.Y(b))
+    circuit2[1] += cirq.X(a)
+    assert circuit2 == expected_circuit
diff --git a/cirq/protocols/json_test_data/__init__.py b/cirq/protocols/json_test_data/__init__.py
new file mode 100644