Repository: quantumlib/Cirq

Issue:
Context manager for disabling validation
Discussed with the hardware team: it might be nice to have a context manager that temporarily disables op validation to speed up circuit construction. This basically involves zeroing the lambda for the duration of the context:

```python
# context managers work something like this, right?
def disable_validation_at_your_own_risk():
    temp = cirq.op.raw_types._validate_qid_shape
    cirq.op.raw_types._validate_qid_shape = lambda val, qubits: None
    yield None
    cirq.op.raw_types._validate_qid_shape = temp

with cirq.disable_validation_at_your_own_risk() as ctx:
    circuit = cirq.Circuit(...)
```

Discussed in Cirq design sync: if we make this, it should live in contrib.


Discussion:

Resolving patch:
diff --git a/cirq-core/cirq/contrib/hacks/disable_validation.py b/cirq-core/cirq/contrib/hacks/disable_validation.py
new file mode 100644
--- /dev/null
+++ b/cirq-core/cirq/contrib/hacks/disable_validation.py
@@ -0,0 +1,39 @@
+# Copyright 2022 The Cirq Developers
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Tools for disabling validation in circuit construction."""
+
+import contextlib
+
+
+@contextlib.contextmanager
+def disable_op_validation(*, accept_debug_responsibility: bool = False):
+    if not accept_debug_responsibility:
+        raise ValueError(
+            "WARNING! Using disable_op_validation with invalid ops can cause "
+            "mysterious and terrible things to happen. cirq-maintainers will "
+            "not help you debug beyond this point!\n"
+            "If you still wish to continue, call this method with "
+            "accept_debug_responsibility=True."
+        )
+
+    from cirq.ops import raw_types
+
+    temp = raw_types._validate_qid_shape
+    raw_types._validate_qid_shape = lambda *args: None
+    try:
+        yield None
+        # ...run context...
+    finally:
+        raw_types._validate_qid_shape = temp


Test patch:
diff --git a/cirq-core/cirq/contrib/hacks/disable_validation_test.py b/cirq-core/cirq/contrib/hacks/disable_validation_test.py
new file mode 100644
--- /dev/null
+++ b/cirq-core/cirq/contrib/hacks/disable_validation_test.py
@@ -0,0 +1,47 @@
+# Copyright 2022 The Cirq Developers
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+
+import cirq
+from cirq.contrib.hacks.disable_validation import disable_op_validation
+
+
+def test_disable_op_validation():
+    q0, q1 = cirq.LineQubit.range(2)
+
+    # Fails normally.
+    with pytest.raises(ValueError, match='Wrong number'):
+        _ = cirq.H(q0, q1)
+
+    # Fails if responsibility is not accepted.
+    with pytest.raises(ValueError, match='mysterious and terrible'):
+        with disable_op_validation():
+            # This does not run - the with condition errors out first.
+            _ = cirq.H(q0, q1)  # coverage: ignore
+
+    # Passes, skipping validation.
+    with disable_op_validation(accept_debug_responsibility=True):
+        op = cirq.H(q0, q1)
+        assert op.qubits == (q0, q1)
+
+    # Validation is restored even on error.
+    with pytest.raises(AssertionError):
+        with disable_op_validation(accept_debug_responsibility=True):
+            assert q0 == q1
+
+    # Future developer: DO NOT REMOVE. This is NOT a duplicate!
+    # It only LOOKS like a duplicate because this is a gross hack :D
+    with pytest.raises(ValueError, match='Wrong number'):
+        _ = cirq.H(q0, q1)