code: def check_grad(
      func,
      inputs=None,
      test_inputs=None,
      eps=1e-6,
      atol=1e-5,
      rtol=1e-3,
      scale=None,
      mean=0,
      mode="higher_order",
      target_devices=None,
      executor_kind="debug",
  ):
      """Perform numerical gradient checking given a relay function.
  
      Compare analytical gradients to numerical gradients derived from two-sided approximation. Note
      that this test may fail if your function input types are not of high enough precision.
  
      Parameters
      ----------
      func : tvm.relay.Function
          The relay function to test.
  
      inputs: List[np.array]
          Optional user-provided input parameters to use. If not given, will generate random normal
          inputs scaled to be close to the chosen epsilon value to avoid numerical precision loss.
  
      test_inputs: List[np.array]
          The inputs to test for gradient matching. Useful in cases where some inputs are not
          differentiable, such as symbolic inputs to dynamic ops. If not given, all inputs are
          tested.
  
      eps: float
          The epsilon value to use for computing numerical gradient approximation.
  
      atol: float
          The absolute tolerance on difference between numerical and analytical gradients. Note that
          this needs to be scaled appropriately relative to the chosen eps and inputs.
  
      rtol: float
          The relative tolerance on difference between numerical and analytical gradients. Note that
          this needs to be scaled appropriately relative to the chosen eps.
  
      scale: float
          The standard deviation of the inputs.
  
      mean: float
          The mean of the inputs.
  
      target_devices: Optional[List[Tuple[tvm.target.Target, tvm.runtime.Device]]]
          A list of targets/devices on which the gradient should be
          tested.  If not specified, will default to `tvm.testing.enabled_targets()`.
  
      """
  
      fwd_func = run_infer_type(func)
      bwd_func = run_infer_type(gradient(fwd_func, mode=mode))
      bwd_func = run_opt_pass(bwd_func, relay.transform.Legalize())
  
      if scale is None:
          scale = 10 * eps
  
      if inputs is None:
          params = fwd_func.params
          # Generate random inputs on the same scale as epsilon to avoid numerical precision loss.
          inputs = [
              _np_randn_from_type(
                  x.checked_type, scale=scale, mean=mean
              )
              for x in params
          ]
  
      if test_inputs is None:
          test_inputs = inputs
  
      if target_devices is None:
          target_devices = enabled_targets()
  
      for target, dev in target_devices:
          # Eval the backward and forward functions
          # TODO(mbs): Evaluate a pair of functions so can share preparation between them.
          bwd_func_compiled = relay.create_executor(
              executor_kind, device=dev, target=target
          ).evaluate(bwd_func)
          fwd_func_compiled = relay.create_executor(
              executor_kind, device=dev, target=target
          ).evaluate(fwd_func)
  
          # Get analytic gradients.
          _, grads = bwd_func_compiled(*inputs)
          grads = [grad.numpy().astype("float64") for grad in grads]
  
          # Throw out gradients we aren't testing
          if inputs != test_inputs:
              tmp = []
              # find the gradient that corresponds to every test input
              for test_input in test_inputs:
                  for i, grad in enumerate(grads):
                      if inputs[i] is test_input:
                          tmp.append(grad)
                          break
              grads = tmp
  
          assert len(grads) > 0, "You must test at least one gradient."
  
          # Get numeric gradients for each dimension of each param, using two-sided approximation.
          approx_grads = []
          for x in test_inputs:
              approx_grad = np.zeros(x.shape)
              for i in np.ndindex(*x.shape):
                  x_i = x[i]
                  x[i] = x_i + eps
                  fwd_plus = (
                      fwd_func_compiled(*inputs)
                      .numpy()
                      .astype("float64")
                  )
                  x[i] = x_i - eps
                  fwd_minus = (
                      fwd_func_compiled(*inputs)
                      .numpy()
                      .astype("float64")
                  )
                  x[i] = x_i
                  approx_grad[i] = np.sum(
                      (fwd_plus - fwd_minus) / (2 * eps)
                  )
              approx_grads.append(approx_grad)
          # Compare gradients by checking that relative difference is below tolerance.
          for grad, approx_grad in zip(grads, approx_grads):
              np.testing.assert_allclose(
                  grad, approx_grad, atol=atol, rtol=rtol
              )
  
imports: from __future__ import absolute_import
  import collections
  import numpy
  import tvm
  from tvm import te
  from tvm import relay
  from tvm.relay import op
  from tvm.relay.prelude import Prelude
  from tvm.testing import enabled_targets
  from .None import mlp
  from .None import resnet
  from .None import resnet_3d
  from .None import dqn
  from .None import dcgan
  from .None import mobilenet
  from .None import lstm
  from .None import inception_v3
  from .None import squeezenet
  from .None import vgg
  from .None import densenet
  from .None import yolo_detection
  from .None import temp_op_attr
  from .None import synthetic
  from .init import create_workload
  from .nat import count, make_nat_value, make_nat_expr
  from .py_converter import to_python, run_as_python
  from ..transform import gradient
label_desc: Use image node_id 4 for calling a global function with example usage: check_grad(func, inputs, test_inputs, eps, atol, rtol, scale, mean, mode, target_devices, executor_kind) without return types