Repository: quantumlib/Cirq

Issue:
Syntactic sugar for cirq.Circuit.from_ops (?)
When using cirq, you'll typically want to create a circuit. As far as I can tell, the most common way of doing that is with `cirq.Circuit.from_ops`. Since this is so common, it *might* be nice to have a shorter alias. Maybe `cirq.ascircuit(...)` by analogy to `np.asarray`. What do you think?



Discussion:
Why don't we just allow arbitrary op_trees in the `Circuit` constructor?
Now that we have the convention about moments going in without being reflowed, `from_ops` is in fact a strict superset of the constructor. Allowing `cirq.Circuit(OP_TREE)` could be reasonable.

Resolving patch:
diff --git a/cirq/circuits/circuit.py b/cirq/circuits/circuit.py
--- a/cirq/circuits/circuit.py
+++ b/cirq/circuits/circuit.py
@@ -95,7 +95,7 @@ class Circuit:
 
     @deprecated_parameter(
         deadline='v0.8',
-        fix='Pass contents positionally or using the "contents=" keyword.',
+        fix='Pass circuit contents positionally (without a keyword).',
         func_name='cirq.Circuit',
         parameter_desc='moments keyword',
         match=lambda args, kwargs: 'moments' in kwargs,
@@ -142,6 +142,7 @@ def device(self, new_device: devices.Device) -> None:
         self._device = new_device
 
     @staticmethod
+    @deprecated(deadline='v0.8.0', fix='use `cirq.Circuit(*ops)` instead.')
     def from_ops(*operations: 'cirq.OP_TREE',
                  strategy: InsertStrategy = InsertStrategy.EARLIEST,
                  device: devices.Device = devices.UNCONSTRAINED_DEVICE
@@ -249,7 +250,7 @@ def __iadd__(self, other):
 
     def __add__(self, other):
         if isinstance(other, list):
-            other = self.from_ops(other)
+            other = Circuit(other)
         if not isinstance(other, type(self)):
             return NotImplemented
         device = (self._device if other.device is devices.UNCONSTRAINED_DEVICE
@@ -1451,9 +1452,12 @@ def final_wavefunction(
         return result.reshape((state_len,))
 
     to_unitary_matrix = deprecated(
-        deadline='v0.7.0', fix='Use `Circuit.unitary()` instead.')(unitary)
+        func_name='Circuit.to_unitary_matrix',
+        deadline='v0.7.0',
+        fix='Use `Circuit.unitary()` instead.')(unitary)
 
     apply_unitary_effect_to_state = deprecated(
+        func_name='Circuit.apply_unitary_effect_to_state',
         deadline='v0.7.0',
         fix="Use `cirq.final_wavefunction(circuit)` or "
         "`Circuit.final_wavefunction()` instead")(final_wavefunction)
@@ -1667,7 +1671,7 @@ def with_noise(self, noise: devices.NoiseModel) -> 'cirq.Circuit':
         c_noisy = Circuit()
         for op_tree in noise.noisy_moments(self, qubits):
             # Keep moments aligned
-            c_noisy += Circuit.from_ops(op_tree)
+            c_noisy += Circuit(op_tree)
         return c_noisy
 
 


Test patch:
diff --git a/cirq/circuits/circuit_test.py b/cirq/circuits/circuit_test.py
--- a/cirq/circuits/circuit_test.py
+++ b/cirq/circuits/circuit_test.py
@@ -23,6 +23,7 @@
 
 import cirq
 import cirq.google as cg
+from cirq._compat_test import capture_logging
 
 
 class _MomentAndOpTypeValidatingDeviceType(cirq.Device):
@@ -1507,22 +1508,42 @@ def _qid_shape_(self):
 
 
 def test_deprecated_circuit_init_parameter_positional_device():
-    c = cirq.Circuit([], cirq.UNCONSTRAINED_DEVICE)
-    assert c == cirq.Circuit(device=cirq.UNCONSTRAINED_DEVICE)
+    with capture_logging() as log:
+        actual = cirq.Circuit([], cirq.UNCONSTRAINED_DEVICE)
+    assert len(log) == 1
+    assert 'positional device parameter' in log[0].getMessage()
+    assert 'deprecated' in log[0].getMessage()
+
+    assert actual == cirq.Circuit(device=cirq.UNCONSTRAINED_DEVICE)
 
 
 def test_deprecated_circuit_init_parameter_moments_keywords():
     a = cirq.LineQubit(0)
-    # pylint: disable=unexpected-keyword-arg
-    c = cirq.Circuit(moments=[cirq.X(a)])
-    # pylint: enable=unexpected-keyword-arg
-    assert c == cirq.Circuit(cirq.X(a))
+    with capture_logging() as log:
+        # pylint: disable=unexpected-keyword-arg
+        actual = cirq.Circuit(moments=[cirq.X(a)])
+        # pylint: enable=unexpected-keyword-arg
+    assert len(log) == 1
+    assert 'moments keyword parameter' in log[0].getMessage()
+    assert 'deprecated' in log[0].getMessage()
+
+    assert actual == cirq.Circuit(cirq.X(a))
 
 
-def test_from_ops():
+def test_deprecated_from_ops():
     a = cirq.NamedQubit('a')
     b = cirq.NamedQubit('b')
 
+    with capture_logging() as log:
+        _ = cirq.Circuit.from_ops()
+    assert len(log) == 1
+    assert 'Circuit.from_ops' in log[0].getMessage()
+    assert 'deprecated' in log[0].getMessage()
+
+    with capture_logging() as log:
+        _ = cirq.Circuit.from_ops()
+    assert len(log) == 0
+
     actual = cirq.Circuit.from_ops(
         cirq.X(a),
         [cirq.Y(a), cirq.Z(b)],
@@ -1530,8 +1551,7 @@ def test_from_ops():
         cirq.X(a),
         [cirq.Z(b), cirq.Y(a)],
     )
-
-    assert actual == cirq.Circuit.from_ops([
+    assert actual == cirq.Circuit([
         cirq.Moment([cirq.X(a), cirq.Z(b)]),
         cirq.Moment([cirq.Y(a)]),
         cirq.Moment([cirq.CZ(a, b)]),
@@ -3132,13 +3152,22 @@ def test_moment_groups():
 
 
 def test_deprecated_to_unitary_matrix():
-    np.testing.assert_allclose(cirq.Circuit().to_unitary_matrix(),
-                               cirq.Circuit().unitary())
+    with capture_logging() as log:
+        np.testing.assert_allclose(cirq.Circuit().to_unitary_matrix(),
+                                   cirq.Circuit().unitary())
+    assert len(log) == 1
+    assert 'to_unitary_matrix' in log[0].getMessage()
+    assert 'deprecated' in log[0].getMessage()
 
 
 def test_deprecated_apply_unitary_effect_to_state():
-    np.testing.assert_allclose(cirq.Circuit().apply_unitary_effect_to_state(),
-                               cirq.Circuit().final_wavefunction())
+    with capture_logging() as log:
+        np.testing.assert_allclose(
+            cirq.Circuit().apply_unitary_effect_to_state(),
+            cirq.Circuit().final_wavefunction())
+    assert len(log) == 1
+    assert 'apply_unitary_effect_to_state' in log[0].getMessage()
+    assert 'deprecated' in log[0].getMessage()
 
 
 def test_moments_property():
diff --git a/cirq/ops/fourier_transform_test.py b/cirq/ops/fourier_transform_test.py
--- a/cirq/ops/fourier_transform_test.py
+++ b/cirq/ops/fourier_transform_test.py
@@ -109,8 +109,8 @@ def test_inverse():
 
 def test_circuit_diagram():
     cirq.testing.assert_has_diagram(
-        cirq.Circuit.from_ops(
-            cirq.decompose_once(cirq.QFT(*cirq.LineQubit.range(4)))), """
+        cirq.Circuit(cirq.decompose_once(cirq.QFT(*cirq.LineQubit.range(4)))),
+        """
 0: ───H───Grad^0.5───────#2─────────────#3─────────────×───
           │              │              │              │
 1: ───────@──────────H───Grad^0.5───────#2─────────×───┼───
@@ -121,7 +121,7 @@ def test_circuit_diagram():
         """)
 
     cirq.testing.assert_has_diagram(
-        cirq.Circuit.from_ops(
+        cirq.Circuit(
             cirq.decompose_once(
                 cirq.QFT(*cirq.LineQubit.range(4), without_reverse=True))), """
 0: ───H───Grad^0.5───────#2─────────────#3─────────────
@@ -134,9 +134,8 @@ def test_circuit_diagram():
         """)
 
     cirq.testing.assert_has_diagram(
-        cirq.Circuit.from_ops(cirq.QFT(*cirq.LineQubit.range(4)),
-                              cirq.inverse(cirq.QFT(*cirq.LineQubit.range(4)))),
-        """
+        cirq.Circuit(cirq.QFT(*cirq.LineQubit.range(4)),
+                     cirq.inverse(cirq.QFT(*cirq.LineQubit.range(4)))), """
 0: ───QFT───QFT^-1───
       │     │
 1: ───#2────#2───────
diff --git a/cirq/sim/sparse_simulator_test.py b/cirq/sim/sparse_simulator_test.py
--- a/cirq/sim/sparse_simulator_test.py
+++ b/cirq/sim/sparse_simulator_test.py
@@ -713,7 +713,7 @@ def test_compute_amplitudes():
 
 def test_compute_amplitudes_bad_input():
     a, b = cirq.LineQubit.range(2)
-    c = cirq.Circuit.from_ops(cirq.X(a), cirq.H(a), cirq.H(b))
+    c = cirq.Circuit(cirq.X(a), cirq.H(a), cirq.H(b))
     sim = cirq.Simulator()
 
     with pytest.raises(ValueError, match='1-dimensional'):