code: def clip_grad(
      grads: Union[
          Generator[NDArray, NDArray, NDArray],
          List[NDArray],
          Tuple[NDArray],
      ],
      clip_method: GradientClippingMethod,
      clip_val: float,
      inplace=True,
  ) -> List[NDArray]:
      """
      Clip gradient values inplace
      :param grads: gradients to be clipped
      :param clip_method: clipping method
      :param clip_val: clipping value. Interpreted differently depending on clipping method.
      :param inplace: modify grads if True, otherwise create NDArrays
      :return: clipped gradients
      """
      output = (
          list(grads)
          if inplace
          else list(nd.empty(g.shape) for g in grads)
      )
      if clip_method == GradientClippingMethod.ClipByGlobalNorm:
          norm_unclipped_grads = global_norm(grads)
          scale = clip_val / (
              norm_unclipped_grads.asscalar() + 1e-8
          )  # todo: use branching operators?
          if scale < 1.0:
              for g, o in zip(grads, output):
                  nd.broadcast_mul(g, nd.array([scale]), out=o)
      elif clip_method == GradientClippingMethod.ClipByValue:
          for g, o in zip(grads, output):
              g.clip(-clip_val, clip_val, out=o)
      elif clip_method == GradientClippingMethod.ClipByNorm:
          for g, o in zip(grads, output):
              nd.broadcast_mul(
                  g,
                  nd.minimum(1.0, clip_val / (g.norm() + 1e-8)),
                  out=o,
              )
      else:
          raise KeyError("Unsupported gradient clipping method")
      return output
  
imports: import inspect
  from typing import Any, Dict, Generator, Iterable, List, Tuple, Union
  from types import ModuleType
  import mxnet
  from mxnet import gluon, nd
  from mxnet.ndarray import NDArray
  import numpy
  from rl_coach.core_types import GradientClippingMethod
label_desc: Use image node_id 11 for calling a global function with example usage: clip_grad(grads, clip_method, clip_val, inplace) and returns: output