code: def test_dropout_double_backward(p, seed, ctx, func_name):
      from nnabla.backward_functions import registry
      from nnabla._dropout_workaround import _get_dropout_mask
  
      # dropout_backward depends on Dropout. The dependency must be kept by the
      # the execution order.
      # 1. Dropout::forward                  (A mask of dropout is calculated.)
      # 2. The forward of dropout_backward   (The mask is used.)
      # 3. The backward of dropout_backward  (The mask is used.)
      # 4. Dropout::backward                 (The mask is used, and then cleared.)
      # This order must be kept when using nnabla.grad. In the current
      # implementation, GradEndFunction keeps this order.
      atol_f = 1e-4
  
      with nn.context_scope(ctx):
          rng = np.random.RandomState(seed)
          init_x = rng.randn(2, 3, 4).astype(np.float32) * 2
          init_dy = rng.randn(*init_x.shape).astype(init_x.dtype)
          init_dy_for_grad = rng.randn(*init_x.shape).astype(
              init_x.dtype
          )
          init_dx = rng.randn(*init_x.shape).astype(init_x.dtype)
          init_for_dx2 = rng.randn(*init_x.shape).astype(init_x.dtype)
  
          #
          # A. Test mask passing
          #
          # Skip p=0 because, in the case, dropout does not happen. mask does not
          # change the results.
          if p != 0:
              with pytest.raises(RuntimeError):
                  x = nn.Variable.from_numpy_array(init_x).apply(
                      need_grad=True
                  )
                  dy = nn.Variable.from_numpy_array(init_dy).apply(
                      need_grad=True
                  )
                  # y = F.dropout(x, p, seed)  # Dropout is required to compute mask.
                  dx = registry["Dropout"](
                      [dy], [x], [None], [None], [None], p, seed
                  )
  
              # Note: y.forward() is required for dx.forward(). However this test
              #       is skipped because the random results are randomly matched
              #       between dx.forward() with and without y.forward(). Therefore
              #       The test result is not reproduced.
  
          #
          # B. Unit test of dropout_backward
          #
          # Graph construction
          x = nn.Variable.from_numpy_array(init_x).apply(need_grad=True)
          dy = nn.Variable.from_numpy_array(init_dy).apply(
              need_grad=True
          )
          y = F.dropout(
              x, p, seed
          )  # Dropout is required to compute mask.
          dx = registry["Dropout"](
              [dy], [x], [None], [None], [None], p, seed
          )
  
          # Execution
          y.forward()  # Dropout is required to compute mask.
          # (y!=0) cannot be used when x includes 0.
          mask = _get_dropout_mask(x).d
          dx.forward()
          # Note: dropout_backward is a composite function. dx.parent is just
          #       a just composing function like MulScalar. Unit tests using
          #       dx.parent.forward and dx.parent.backward are meaningless.
          #       By the same reason, test of accumulation is nonsense.
  
          # Reference
          ref_dx = ref_dropout_backward(init_dy, mask, p)
  
          # Test
          assert_allclose(
              dx.d,
              ref_dx,
              atol=atol_f,
              err_msg="Wrong output values of dropout_backward.",
          )
  
          #
          # C. Test the forward of dropout_backward by using nnabla.grad
          #
          # Graph construction
          x = nn.Variable.from_numpy_array(init_x).apply(need_grad=True)
          y = F.dropout(x, p, seed)
          dx = nn.grad(y, x, grad_outputs=[init_dy_for_grad])[0]
          # Note: In NNabla 1.22.0, if use grad_outputs=X, nn.grad separate
          #       np.ndarray X into small arrays by self._force_list.
          #       For example, X = np.array([[5, 6], [7, 8]]) is separated
          #       into [np.array([5, 6]), np.array(7, 8)]. Then Mul2 inserted by
          #       nn.grad uses np.array([5, 6]) as dy, and broadcasts it to
          #       the np.array([[5, 6], [5, 6]]). Finally, the forward execution
          #       is finished, but the result values are wrong.
  
          # Execution
          dx.forward(clear_buffer=True)
  
          # Reference
          mask = _get_dropout_mask(x).d
          ref_dx = ref_dropout_backward(init_dy_for_grad, mask, p)
  
          # Test
          assert_allclose(
              dx.d,
              ref_dx,
              atol=atol_f,
              err_msg="Wrong output values of Dropout of nn.grad.",
          )
  
          #
          # D. Test the backward of dropout_backward by using nnabla.grad
          #
          # The numerical grad by using scipy.approx_fprime cannot be performed
          # because Dropout has randomness and changes the results during
          # the repeated forward computation.
  
          # Graph construction
          x = nn.Variable.from_numpy_array(init_x).apply(need_grad=True)
          y = F.dropout(x, p, seed)
          dx = nn.grad(y, x, grad_outputs=[init_dy_for_grad])[0]
          y_dx = (
              y + dx
          )  # replaceable with F.sink(y, dx, one_input_grad=False)
  
          # Execution
          x.g = init_dx  # Accumulation
          y_dx.forward(clear_no_need_grad=True)
          mask = _get_dropout_mask(x).d  # Store mask before the clear
          y_dx.backward(init_for_dx2, clear_buffer=True)
  
          # Reference
          ref_dx = (
              ref_dropout_double_backward(init_for_dx2, mask, p)
              + init_dx
          )
  
          # Test
          assert_allclose(
              x.g,
              ref_dx,
              atol=atol_f,
              err_msg="Wrong output values of double backward of "
              "Dropout by nn.grad.",
          )
  
file_path: nnabla/python/test/function/test_dropout.py
imports: from six.moves import range
  import pytest
  import numpy
  import nnabla
  import nnabla.functions
  from nbla_test_utils import list_context
  from nnabla.testing import assert_allclose
label_desc: Use image node_id 5 for calling a global function with example usage: test_dropout_double_backward(p, seed, ctx, func_name) without return types