Repository: quantumlib/cirq
Issue: optimize_for_target_gateset OOMs on large CircuitOps

**Description of the issue**

When a circuit containing a large (in this case, 17 qubits & depth 20) CircuitOp is passed to `optimize_for_target_gateset`, it generates an OOM, possibly crashing the device. This circuit is well within the realm of "easy to simulate", so we shouldn't be OOMing or should at least warn users that this has a lower threshold than simulation.

**How to reproduce the issue**

```
qnum = 17
qubits = cirq.LineQubit.range(qnum)
subcircuit = cirq.FrozenCircuit(
    *[
      cirq.CX(qubits[i], qubits[i+1])
      for i in range(0, qnum-1, 2)
    ],
    *[
      cirq.CZ(qubits[i], qubits[i+1])
      for i in range(1, qnum-1, 2)
    ],
)
circuit = cirq.Circuit(
    cirq.CircuitOperation(subcircuit).repeat(10),
    cirq.measure(*qubits, key='out')
)
print(circuit)
# OOM: cannot allocate 256GB
supported_circuit = cirq.optimize_for_target_gateset(
    circuit,
    context=cirq.TransformerContext(deep=True),
    gateset=cirq.CZTargetGateset(),
)
```

<details>

```
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "~/git/cirq-realnoise-docs/cirq-core/cirq/transformers/transformer_api.py", line 380, in func_with_logging
    return _transform_and_log(
  File "~/git/cirq-realnoise-docs/cirq-core/cirq/transformers/transformer_api.py", line 439, in _transform_and_log
    transformed_circuit = _run_transformer_on_circuit(
  File "~/git/cirq-realnoise-docs/cirq-core/cirq/transformers/transformer_api.py", line 425, in _run_transformer_on_circuit
    return func(mutable_circuit if mutable_circuit else circuit, **kwargs)
  File "~/git/cirq-realnoise-docs/cirq-core/cirq/transformers/optimize_for_target_gateset.py", line 143, in optimize_for_target_gateset
    circuit = transformer(circuit, context=context)
  File "~/git/cirq-realnoise-docs/cirq-core/cirq/transformers/transformer_api.py", line 380, in func_with_logging
    return _transform_and_log(
  File "~/git/cirq-realnoise-docs/cirq-core/cirq/transformers/transformer_api.py", line 439, in _transform_and_log
    transformed_circuit = _run_transformer_on_circuit(
  File "~/git/cirq-realnoise-docs/cirq-core/cirq/transformers/transformer_api.py", line 425, in _run_transformer_on_circuit
    return func(mutable_circuit if mutable_circuit else circuit, **kwargs)
  File "~/git/cirq-realnoise-docs/cirq-core/cirq/transformers/drop_negligible_operations.py", line 54, in drop_negligible_operations
    return transformer_primitives.map_operations(
  File "~/git/cirq-realnoise-docs/cirq-core/cirq/transformers/transformer_primitives.py", line 143, in map_operations
    return map_moments(
  File "~/git/cirq-realnoise-docs/cirq-core/cirq/transformers/transformer_primitives.py", line 87, in map_moments
    return _create_target_circuit_type(
  File "~/git/cirq-realnoise-docs/cirq-core/cirq/transformers/transformer_primitives.py", line 43, in _create_target_circuit_type
    circuits.Circuit(ops)
  File "~/git/cirq-realnoise-docs/cirq-core/cirq/circuits/circuit.py", line 1707, in __init__
    self.append(contents, strategy=strategy)
  File "~/git/cirq-realnoise-docs/cirq-core/cirq/circuits/circuit.py", line 2254, in append
    self.insert(len(self._moments), moment_or_operation_tree, strategy)
  File "~/git/cirq-realnoise-docs/cirq-core/cirq/circuits/circuit.py", line 1969, in insert
    moments_and_operations = list(
  File "~/git/cirq-realnoise-docs/cirq-core/cirq/ops/op_tree.py", line 134, in flatten_to_ops_or_moments
    yield from flatten_to_ops_or_moments(subtree)
  File "~/git/cirq-realnoise-docs/cirq-core/cirq/ops/op_tree.py", line 133, in flatten_to_ops_or_moments
    for subtree in root:
  File "~/git/cirq-realnoise-docs/cirq-core/cirq/ops/op_tree.py", line 169, in <genexpr>
    return iter_transformation(
  File "~/git/cirq-realnoise-docs/cirq-core/cirq/transformers/transformer_primitives.py", line 88, in <genexpr>
    (map_func(mutable_circuit[i], i) for i in range(len(mutable_circuit))), circuit
  File "~/git/cirq-realnoise-docs/cirq-core/cirq/transformers/transformer_primitives.py", line 145, in <lambda>
    lambda m, i: [circuits.Moment(apply_map(op, i) for op in m.operations)],
  File "~/git/cirq-realnoise-docs/cirq-core/cirq/circuits/moment.py", line 93, in __init__
    self._operations = tuple(op_tree.flatten_to_ops(contents))
  File "~/git/cirq-realnoise-docs/cirq-core/cirq/ops/op_tree.py", line 113, in flatten_to_ops
    yield from flatten_to_ops(subtree)
  File "~/git/cirq-realnoise-docs/cirq-core/cirq/ops/op_tree.py", line 112, in flatten_to_ops
    for subtree in root:
  File "~/git/cirq-realnoise-docs/cirq-core/cirq/transformers/transformer_primitives.py", line 145, in <genexpr>
    lambda m, i: [circuits.Moment(apply_map(op, i) for op in m.operations)],
  File "~/git/cirq-realnoise-docs/cirq-core/cirq/transformers/transformer_primitives.py", line 130, in apply_map
    c = circuits.FrozenCircuit(map_func(op, idx))
  File "~/git/cirq-realnoise-docs/cirq-core/cirq/transformers/drop_negligible_operations.py", line 51, in map_func
    op if protocols.is_measurement(op) or protocols.trace_distance_bound(op) > atol else []
  File "~/git/cirq-realnoise-docs/cirq-core/cirq/protocols/trace_distance_bound.py", line 73, in trace_distance_bound
    result = strat(val)
  File "~/git/cirq-realnoise-docs/cirq-core/cirq/protocols/trace_distance_bound.py", line 98, in _strat_distance_from_unitary
    u = unitary_protocol.unitary(val, default=None)
  File "~/git/cirq-realnoise-docs/cirq-core/cirq/protocols/unitary_protocol.py", line 119, in unitary
    result = strat(val)
  File "~/git/cirq-realnoise-docs/cirq-core/cirq/protocols/unitary_protocol.py", line 183, in _strat_unitary_from_decompose
    state = qis.eye_tensor(val_qid_shape, dtype=np.complex128)
  File "~/git/cirq-realnoise-docs/cirq-core/cirq/qis/states.py", line 1076, in eye_tensor
    identity = np.eye(np.prod(half_shape, dtype=np.int64).item(), dtype=dtype)
  File "~/.virtualenvs/cirq/lib/python3.9/site-packages/numpy/lib/twodim_base.py", line 209, in eye
    m = zeros((N, M), dtype=dtype, order=order)
numpy.core._exceptions._ArrayMemoryError: Unable to allocate 256. GiB for an array with shape (131072, 131072) and data type complex128
```

</details>

**Cirq version**
0.15.0.dev


Changed files:
cirq-core/cirq/transformers/drop_negligible_operations_test.py, cirq-core/cirq/transformers/drop_negligible_operations.py

diff --git a/cirq-core/cirq/transformers/drop_negligible_operations.py b/cirq-core/cirq/transformers/drop_negligible_operations.py
index 51c11366..1e609100 100644
--- a/cirq-core/cirq/transformers/drop_negligible_operations.py
+++ b/cirq-core/cirq/transformers/drop_negligible_operations.py
@@ -48,7 +48,11 @@ def drop_negligible_operations(
 
     def map_func(op: 'cirq.Operation', _: int) -> 'cirq.OP_TREE':
         return (
-            op if protocols.is_measurement(op) or protocols.trace_distance_bound(op) > atol else []
+            op
+            if protocols.num_qubits(op) > 10
+            or protocols.is_measurement(op)
+            or protocols.trace_distance_bound(op) > atol
+            else []
         )
 
     return transformer_primitives.map_operations(
diff --git a/cirq-core/cirq/transformers/drop_negligible_operations_test.py b/cirq-core/cirq/transformers/drop_negligible_operations_test.py
index cf2371f6..bc444139 100644
--- a/cirq-core/cirq/transformers/drop_negligible_operations_test.py
+++ b/cirq-core/cirq/transformers/drop_negligible_operations_test.py
@@ -94,3 +94,16 @@ def test_recursively_runs_inside_circuit_ops_deep():
     cirq.testing.assert_same_circuits(
         cirq.drop_negligible_operations(c_orig, context=context, atol=0.001), c_expected
     )
+
+
+def test_ignores_large_ops():
+    qnum = 20
+    qubits = cirq.LineQubit.range(qnum)
+    subcircuit = cirq.FrozenCircuit(cirq.X.on_each(*qubits))
+    circuit = cirq.Circuit(
+        cirq.CircuitOperation(subcircuit).repeat(10), cirq.measure(*qubits, key='out')
+    )
+    cirq.testing.assert_same_circuits(
+        circuit,
+        cirq.drop_negligible_operations(circuit, context=cirq.TransformerContext(deep=True)),
+    )