code: def compute_analytical_and_numerical_grad(
      f,
      inputs,
      outputs,
      inputs0,
      vgrads,
      epsilon=1e-8,
      rng=None,
      ref_grad=None,
  ):
      """Compute both analytical grad and numerical grad
      using given function
  
      f: function to test
      inputs: function input variable
      outputs: function output variable
      inputs0: function inputs to calculate numerical grad
      vgrads: initial grads of output variable
      epsilon: small value to calculate numerical grad
      rng: random number generator
      """
      if rng is None:
          rng = np.random.RandomState(np.random.randint(1000))
  
      from scipy import optimize
  
      for i in inputs:
          if i is None:  # Optional argument
              continue
          i.g = randn(rng, *i.shape)
      for o, d in zip(outputs, vgrads):
          o.g = d
  
      def func(x0):
          bind = 0
          backups = []
          vinputs = []
          for i, i0 in zip(inputs, inputs0):
              if i is None:  # Optional argument
                  backups.append(None)
                  continue
              vinputs += [i]
              if i0 is not None:  # Not need backward
                  i.d[...] = x0[bind : bind + i.size].reshape(i.shape)
                  bind += i.size
              backups.append(i.d.copy())
  
          f.forward(vinputs, outputs)
          for ind, i in enumerate(inputs):
              if i is None:  # Optional argument
                  continue
              i.d[...] = backups[ind]
          return sum([np.sum(o.g * o.d) for o in outputs])
  
      def grad(x0):
          bind = 0
          backups = []
          vinputs = []
          for i, i0 in zip(inputs, inputs0):
              if i is None:  # Optional argument
                  continue
              vinputs += [i]
              if i0 is not None:  # Not need backward
                  i.d[...] = x0[bind : bind + i.size].reshape(i.shape)
                  backups.append(i.g.copy())
                  bind += i.size
              else:
                  assert not i.need_grad
          f.setup(vinputs, outputs)
          # Need to call reset output grads since output might be inplaced
          for o, d in zip(outputs, vgrads):
              o.g = d
          f.forward(vinputs, outputs)
          f.backward(vinputs, outputs)
  
          bind = 0
          g = np.zeros_like(x0)
          ind = 0
          for i, i0 in zip(inputs, inputs0):
              if i is None:  # Optional argument
                  continue
              if i0 is not None:  # Not need backward
                  g[bind : bind + i.size] = (
                      i.g.flatten() - backups[ind].flatten()
                  )
                  i.g[...] = backups[ind]
                  bind += i.size
                  ind += 1
          return g
  
      inputs0_c = np.concatenate(
          [i0.flatten() for i0 in inputs0 if i0 is not None]
      )
      analytical_grad = grad(inputs0_c)
      numerical_grad = None
      if not ref_grad:
          numerical_grad = optimize.approx_fprime(
              inputs0_c, func, epsilon
          )
      return analytical_grad, numerical_grad
  
imports: from nnabla.utils import nnabla_pb2
  from six.moves import filter
  import copy
  import nnabla
  import nnabla.ext_utils
  import nnabla.functions
  import nnabla.utils.converter
  import nnabla.dtypes
  from nnabla.testing import assert_allclose
  import numpy
  import numpy
  import platform
label_desc: Use image node_id 9 for calling a global function with example usage: compute_analytical_and_numerical_grad(f, inputs, outputs, inputs0, vgrads, epsilon, rng, ref_grad) and returns: analytical_grad,  numerical_grad, sum, g