Repository: quantumlib/Cirq

Issue:
If decompose's on_stuck_raise is a lambda that returns None, no error should be raised
Currently, passing in None instead of a lambda has this behavior. But this can result in duplicate logic when passing in the lambda or None and implementing the lambda.

Don't forget to update the argument type and to test the new behavior.


Discussion:
Hi @iamvamsi, are you still working on this? If not, I'm happy to do it.
@andbe91 Feel free to take over this one.
@andbe91 : I haven't yet started on this. Please feel free to take it.

Resolving patch:
diff --git a/cirq/protocols/decompose.py b/cirq/protocols/decompose.py
--- a/cirq/protocols/decompose.py
+++ b/cirq/protocols/decompose.py
@@ -164,7 +164,11 @@ def decompose(
                                         NotImplementedType,
                                         'cirq.OP_TREE']] = None,
     keep: Callable[['cirq.Operation'], bool] = None,
-    on_stuck_raise=_value_error_describing_bad_operation
+    on_stuck_raise: Union[None,
+                          Exception,
+                          Callable[['cirq.Operation'],
+                                   Union[None, Exception]]]
+    = _value_error_describing_bad_operation
 ) -> List['cirq.Operation']:
     """Recursively decomposes a value into `cirq.Operation`s meeting a criteria.
 
@@ -189,9 +193,10 @@ def decompose(
             also can't be kept, `on_stuck_raise` is used to determine what error
             to raise. `on_stuck_raise` can either directly be an `Exception`, or
             a method that takes the problematic operation and returns an
-            `Exception`. If `on_stuck_raise` is set to `None`, undecomposable
-            operations are simply silently kept. `on_stuck_raise` defaults to a
-            `ValueError` describing the unwanted undecomposable operation.
+            `Exception`. If `on_stuck_raise` is set to `None` or a method that
+            returns `None`, undecomposable operations are simply silently kept.
+            `on_stuck_raise` defaults to a `ValueError` describing the unwanted
+            undecomposable operation.
 
     Returns:
         A list of operations that the given value was decomposed into. If
@@ -254,8 +259,10 @@ def decomposer(op):
         if keep is not None and on_stuck_raise is not None:
             if isinstance(on_stuck_raise, Exception):
                 raise on_stuck_raise
-            else:
-                raise on_stuck_raise(item)
+            elif callable(on_stuck_raise):
+                error = on_stuck_raise(item)
+                if error is not None:
+                    raise error
 
         output.append(item)
 


Test patch:
diff --git a/cirq/protocols/decompose_test.py b/cirq/protocols/decompose_test.py
--- a/cirq/protocols/decompose_test.py
+++ b/cirq/protocols/decompose_test.py
@@ -184,6 +184,9 @@ def test_decompose_on_stuck_raise():
     # Or you say you're fine.
     assert cirq.decompose(
         no_method, keep=lambda _: False, on_stuck_raise=None) == [no_method]
+    assert cirq.decompose(
+        no_method, keep=lambda _: False,
+        on_stuck_raise=lambda _: None) == [no_method]
     # You can customize the error.
     with pytest.raises(TypeError, match='test'):
         _ = cirq.decompose(
@@ -195,6 +198,7 @@ def test_decompose_on_stuck_raise():
             on_stuck_raise=lambda op: NotImplementedError('op {!r}'.format(
                 op)))
 
+
     # There's a nice warning if you specify `on_stuck_raise` but not `keep`.
     with pytest.raises(ValueError, match='on_stuck_raise'):
         assert cirq.decompose([], on_stuck_raise=None)