Fix for double backwards tests (#18190)
authorIgor Fedan <ifedan@fb.com>
Mon, 1 Apr 2019 19:30:29 +0000 (12:30 -0700)
committerFacebook Github Bot <facebook-github-bot@users.noreply.github.com>
Mon, 1 Apr 2019 19:33:30 +0000 (12:33 -0700)
Summary:
If none of the outputs require_grad, we don't actually check gradgrad, instead we will check that their numerical gradients are 0.
Pull Request resolved: https://github.com/pytorch/pytorch/pull/18190

Differential Revision: D14563388

Pulled By: ifedan

fbshipit-source-id: a4eb94c9eb60f14dbe6986cd8cef1fe78a7bc839

test/test_autograd.py
torch/autograd/gradcheck.py

index 0a2c08e..7bbb868 100644 (file)
@@ -2285,6 +2285,15 @@ class TestAutograd(TestCase):
         _test_complex((1, 2, 3, 4, 2), 2)
         _test_complex((2, 1, 3, 4, 3, 2), 3)
 
+    def test_gradcheck_fail_when_no_differentiable_outputs_and_num_grad_not_zero(self):
+        def autograd_fn(input):
+            output = torch.detach(input)
+            self.assertFalse(output.requires_grad)
+            return output
+
+        f_args_variable = torch.ones(S, S, requires_grad=True)
+        self.assertRaisesRegex(RuntimeError, 'Numerical gradient for function expected to be zero', lambda: gradcheck(autograd_fn, f_args_variable, eps=1e-6, atol=PRECISION))
+
     def test_variable_traverse(self):
         def get_out_and_unrefed_cycle():
             inp = torch.randn(10, requires_grad=True)
index 20ea632..6dd4cc7 100644 (file)
@@ -203,11 +203,12 @@ def gradcheck(func, inputs, eps=1e-6, atol=1e-5, rtol=1e-3, raise_exception=True
 
     tupled_inputs = _as_tuple(inputs)
     if any(t.is_sparse for t in tupled_inputs if isinstance(t, torch.Tensor)) and not check_sparse_nnz:
-        fail_test('gradcheck expects all tensor inputs '
+        return fail_test('gradcheck expects all tensor inputs '
                   'are dense when check_sparse_nnz is set to False.')
 
     # Make sure that gradients are saved for all inputs
     any_input_requiring_grad = False
+    some_input_not_requiring_grad = False
     for inp in tupled_inputs:
         if isinstance(inp, torch.Tensor):
             if inp.requires_grad:
@@ -218,13 +219,30 @@ def gradcheck(func, inputs, eps=1e-6, atol=1e-5, rtol=1e-3, raise_exception=True
                         'This check will likely fail if all the inputs are '
                         'not of double precision floating point. ')
                 any_input_requiring_grad = True
+            else:
+                some_input_not_requiring_grad = True
             inp.retain_grad()
     if not any_input_requiring_grad:
         raise ValueError(
             'gradcheck expects at least one input tensor to require gradient, '
             'but none of the them have requires_grad=True.')
-
-    output = _differentiable_outputs(func(*tupled_inputs))
+        if some_input_not_requiring_grad:
+            raise ValueError(
+                'gradcheck expects if at least one input tensor is required gradient, '
+                'then all other inputs should have requires_grad=True.')
+
+    func_out = func(*tupled_inputs)
+    output = _differentiable_outputs(func_out)
+
+    if not output:
+        for i, o in enumerate(func_out):
+            def fn(input):
+                return _as_tuple(func(*input))[i]
+            numerical = get_numerical_jacobian(fn, tupled_inputs, eps=eps)
+            for n in numerical:
+                if len(torch.nonzero(n)) > 0:
+                    return fail_test('Numerical gradient for function expected to be zero')
+        return True
 
     for i, o in enumerate(output):
         if not o.requires_grad: