class_docstr: Customize differentiation in autograd.
  
  If you don't want to use the gradients computed by the default
  chain-rule, you can use Function to customize differentiation for
  computation. You define your computation in
  the forward method and provide the customized differentiation
  in the backward method. During gradient computation, autograd will
  use the user-defined backward function instead of the default chain-rule.
  You can also cast to numpy array and back for some operations in
  forward and backward.
  
  For example, a stable sigmoid function can be defined as::
  
      class sigmoid(mx.autograd.Function):
          def forward(self, x):
              y = 1 / (1 + mx.nd.exp(-x))
              self.save_for_backward(y)
              return y
  
          def backward(self, dy):
              # backward takes as many inputs as forward's return value,
              # and returns as many NDArrays as forward's arguments.
              y, = self.saved_tensors
              return dy * y * (1-y)
  
  Then, the function can be used in the following way::
  
      func = sigmoid()
      x = mx.nd.random.uniform(shape=(10,))
      x.attach_grad()
  
      with mx.autograd.record():
          m = func(x)
          m.backward()
      dx = x.grad.asnumpy()
class_docstr_tok: ["Customize","differentiation","in","autograd",".","If","you","do","n't","want","to","use","the","gradients","computed","by","the","default","chain-rule",",","you","can","use","Function","to","customize","differentiation","for","computation",".","You","define","your","computation","in","the","forward","method","and","provide","the","customized","differentiation","in","the","backward","method",".","During","gradient","computation",",","autograd","will","use","the","user-defined","backward","function","instead","of","the","default","chain-rule",".","You","can","also","cast","to","numpy","array","and","back","for","some","operations","in","forward","and","backward",".","For","example",",","a","stable","sigmoid","function","can","be","defined","as",":",":","class","sigmoid","(","mx.autograd.Function",")",":","def","forward","(","self",",","x",")",":","y","=","1","\/","(","1","+","mx.nd.exp","(","-x",")",")","self.save_for_backward","(","y",")","return","y","def","backward","(","self",",","dy",")",":","#","backward","takes","as","many","inputs","as","forward","'s","return","value",",","#","and","returns","as","many","NDArrays","as","forward","'s","arguments",".","y",",","=","self.saved_tensors","return","dy","*","y","*","(","1-y",")","Then",",","the","function","can","be","used","in","the","following","way",":",":","func","=","sigmoid","(",")","x","=","mx.nd.random.uniform","(","shape=","(","10",",",")",")","x.attach_grad","(",")","with","mx.autograd.record","(",")",":","m","=","func","(","x",")","m.backward","(",")","dx","=","x.grad.asnumpy","(",")"]
code: def __call__(self, *inputs):
      assert not self._used, (
          "Each Function instance can only be called once. "
          "Please create another instance."
      )
      self._used = True
  
      prev_recording = set_recording(False)
      outputs = self.forward(*inputs)
      set_recording(prev_recording)
  
      if not prev_recording:
          return outputs
  
      ret_outputs = outputs
      if isinstance(outputs, NDArray):
          outputs = (outputs,)
  
      key = Function._registry.inc()
      if is_np_array():
          from .numpy import ndarray
  
          array_cls = ndarray
      else:
          array_cls = NDArray
  
      def backward_entry(
          num_ograds, num_igrads, ptrs, reqs, is_train, _
      ):
          """entry point for backward."""
          # pylint: disable=W0613
          try:
              output_grads = [
                  array_cls(
                      ctypes.cast(i, NDArrayHandle), writable=False
                  )
                  for i in ptrs[:num_ograds]
              ]
              input_grads = [
                  array_cls(
                      ctypes.cast(i, NDArrayHandle), writable=True
                  )
                  for i in ptrs[num_ograds : num_ograds + num_igrads]
              ]
              reqs = [reqs[i] for i in range(num_igrads)]
              rets = self.backward(*output_grads)
              if isinstance(rets, array_cls):
                  rets = (rets,)
              assert len(rets) == len(input_grads), (
                  f"{self.__class__.name}.backward must return exactly the same number "
                  "of NDArrays as the number of NDArrays arguments to forward."
                  f"Expecting {len(input_grads)} got {len(rets)}"
              )
              for igrad, ret, req in zip(input_grads, rets, reqs):
                  assert isinstance(
                      ret, array_cls
                  ), f"autograd.Function.backward must return NDArrays, not {type(ret)}"
                  if req == 0:  # null
                      return True
                  elif req in (1, 2):  # write or inplace
                      igrad[:] = ret
                  elif req == "add":
                      igrad[:] += ret
          except Exception:  # pylint: disable=broad-except
              print(
                  f"Error in Function.backward: {traceback.format_exc()}"
              )
              return False
          return True
  
      def delete_entry(_):
          """C Callback for CustomFunction::delete"""
          try:
              del Function._registry.ref_holder[key]
          except Exception:  # pylint: disable=broad-except
              print(
                  f"Error in autograd.Function.delete: {traceback.format_exc()}"
              )
              return False
          return True
  
      callbacks = [
          Function._bwd_functype(backward_entry),
          Function._del_functype(delete_entry),
      ]
      callbacks = [cast(i, CFUNCTYPE(c_int)) for i in callbacks]
      context = MXCallbackList(
          c_int(len(callbacks)),
          cast(
              c_array(CFUNCTYPE(c_int), callbacks),
              POINTER(CFUNCTYPE(c_int)),
          ),
          cast(
              c_array(c_void_p, [None] * len(callbacks)),
              POINTER(c_void_p),
          ),
      )
      Function._registry.ref_holder[key] = context
      check_call(
          _LIB.MXCustomFunctionRecord(
              c_int(len(inputs)),
              c_handle_array(inputs),
              c_int(len(outputs)),
              c_handle_array(outputs),
              ctypes.byref(context),
          )
      )
  
      return ret_outputs
  
imports: from array import array
  from threading import Lock
  import traceback
  import ctypes
  from ctypes import c_int, c_void_p, CFUNCTYPE, POINTER, cast
  from .base import _LIB, check_call, string_types, mx_uint
  from .base import NDArrayHandle, c_array, c_handle_array, c_array_buf, MXCallbackList, SymbolHandle
  from .ndarray import NDArray, _ndarray_cls
  from .ndarray import _GRAD_REQ_MAP
  from .symbol import Symbol
  from .util import is_np_array
label_desc: Use image node_id 3 for calling the Function obj's underlying member method code with example usage: obj.__call__() and returns: ret_outputs, outputs, True, True, False, False, True