Repository: quantumlib/Cirq

Issue:
Printing Moment containing classical control fails
**Description of the issue**

A classically controlled op causes moment printing to fail. The op itself renders correctly, and a circuit containing the moment renders correctly. Just the moment itself fails to render.

**How to reproduce the issue**

```python
op = cirq.X(cirq.NamedQubit('q')).with_classical_controls('m')
m = cirq.Moment(op)
print(op)
print(cirq.Circuit(m))
print(m)
```

```
X(q).with_classical_controls(m)


q: ───X───
      ║
m: ═══^═══


ValueError: Wanted diagram info from cirq.ClassicallyControlledOperation(cirq.X(cirq.NamedQubit('q')), [cirq.KeyCondition(cirq.MeasurementKey(name='m'))]) for [cirq.NamedQubit('q')]) but got cirq.CircuitDiagramInfo(wire_symbols=('X', '^'), exponent=1.0, connected=True, exponent_qubit_index=1, auto_exponent_parens=True)

cirq-core/cirq/protocols/circuit_diagram_info_protocol.py:345: ValueError
```

v0.15


Discussion:
The root cause is `label_map` is not passed in by `Moment.to_text_diagram`. Thus `ClassicallyControlledOperation._circuit_diagram_info_` needs to handle `args.label_map is None` in a couple places. In particular `control_count` should be zero if there's no label map, and the condition labels should always be added if there's no label map.

Resolving patch:
diff --git a/cirq-core/cirq/ops/classically_controlled_operation.py b/cirq-core/cirq/ops/classically_controlled_operation.py
--- a/cirq-core/cirq/ops/classically_controlled_operation.py
+++ b/cirq-core/cirq/ops/classically_controlled_operation.py
@@ -149,9 +149,13 @@ def _circuit_diagram_info_(
         sub_info = protocols.circuit_diagram_info(self._sub_operation, sub_args, None)
         if sub_info is None:
             return NotImplemented  # coverage: ignore
-        control_count = len({k for c in self._conditions for k in c.keys})
-        wire_symbols = sub_info.wire_symbols + ('^',) * control_count
-        if any(not isinstance(c, value.KeyCondition) for c in self._conditions):
+        control_label_count = 0
+        if args.label_map is not None:
+            control_label_count = len({k for c in self._conditions for k in c.keys})
+        wire_symbols = sub_info.wire_symbols + ('^',) * control_label_count
+        if control_label_count == 0 or any(
+            not isinstance(c, value.KeyCondition) for c in self._conditions
+        ):
             wire_symbols = (
                 wire_symbols[0]
                 + '(conditions=['
@@ -160,9 +164,9 @@ def _circuit_diagram_info_(
             ) + wire_symbols[1:]
         exponent_qubit_index = None
         if sub_info.exponent_qubit_index is not None:
-            exponent_qubit_index = sub_info.exponent_qubit_index + control_count
+            exponent_qubit_index = sub_info.exponent_qubit_index + control_label_count
         elif sub_info.exponent is not None:
-            exponent_qubit_index = control_count
+            exponent_qubit_index = control_label_count
         return protocols.CircuitDiagramInfo(
             wire_symbols=wire_symbols,
             exponent=sub_info.exponent,


Test patch:
diff --git a/cirq-core/cirq/ops/classically_controlled_operation_test.py b/cirq-core/cirq/ops/classically_controlled_operation_test.py
--- a/cirq-core/cirq/ops/classically_controlled_operation_test.py
+++ b/cirq-core/cirq/ops/classically_controlled_operation_test.py
@@ -1017,3 +1017,19 @@ def test_commutes():
     assert not cirq.commutes(
         cirq.X(q0).with_classical_controls('a'), cirq.H(q0).with_classical_controls('a')
     )
+
+
+def test_moment_diagram():
+    a, _, c, d = cirq.GridQubit.rect(2, 2)
+    m = cirq.Moment(cirq.CZ(a, d), cirq.X(c).with_classical_controls('m'))
+    assert (
+        str(m).strip()
+        == """
+  ╷ 0                 1
+╶─┼─────────────────────
+0 │ @─────────────────┐
+  │                   │
+1 │ X(conditions=[m]) @
+  │
+    """.strip()
+    )