class_docstr: A gradient checker in Python.
  
  This is not the most efficient way to check gradients, as the Python
  interface will involve a lot of copies back and forth operations. Use at your
  own risk.
docstr: Checks the operator in a very simple fashion by stacking a sum of
  squares on the top.
  
  Inputs:
    op: the operator to be checked.
    inputs: the input data in numpy arrays.
    input_to_check: an index specifying which input blob we should
        check.
    outputs_with_grads: indices specifying which output blobs will we
        need to check gradients with. For these outputs, we will collect a
        squared sum and also feed in their gradients.
    grad_operator: the gradient operator. If not given, we will get the
        gradient operator from the gradient registry.
    input_device_options: an optional mapping from input names to
        DeviceOptions (to override the default DeviceOption)
    ensure_outputs_are_inferred: if set will assert that the gradient output
        shapes matches the inferred shapes
  Outputs:
    boolean: True if it passes, False if it does not pass.
docstr_tok: ["Checks","the","operator","in","a","very","simple","fashion","by","stacking","a","sum","of","squares","on","the","top",".","Inputs",":","op",":","the","operator","to","be","checked",".","inputs",":","the","input","data","in","numpy","arrays",".","input_to_check",":","an","index","specifying","which","input","blob","we","should","check",".","outputs_with_grads",":","indices","specifying","which","output","blobs","will","we","need","to","check","gradients","with",".","For","these","outputs",",","we","will","collect","a","squared","sum","and","also","feed","in","their","gradients",".","grad_operator",":","the","gradient","operator",".","If","not","given",",","we","will","get","the","gradient","operator","from","the","gradient","registry",".","input_device_options",":","an","optional","mapping","from","input","names","to","DeviceOptions","(","to","override","the","default","DeviceOption",")","ensure_outputs_are_inferred",":","if","set","will","assert","that","the","gradient","output","shapes","matches","the","inferred","shapes","Outputs",":","boolean",":","True","if","it","passes",",","False","if","it","does","not","pass","."]
code: def CheckSimple(
      self,
      op,
      inputs,
      input_to_check,
      outputs_with_grads,
      grad_ops=None,
      input_device_options=None,
      ensure_outputs_are_inferred=False,
  ):
      """Checks the operator in a very simple fashion by stacking a sum of
      squares on the top.
  
      Inputs:
        op: the operator to be checked.
        inputs: the input data in numpy arrays.
        input_to_check: an index specifying which input blob we should
            check.
        outputs_with_grads: indices specifying which output blobs will we
            need to check gradients with. For these outputs, we will collect a
            squared sum and also feed in their gradients.
        grad_operator: the gradient operator. If not given, we will get the
            gradient operator from the gradient registry.
        input_device_options: an optional mapping from input names to
            DeviceOptions (to override the default DeviceOption)
        ensure_outputs_are_inferred: if set will assert that the gradient output
            shapes matches the inferred shapes
      Outputs:
        boolean: True if it passes, False if it does not pass.
      """
      # Entering the checker workspace
      old_ws_name = workspace.CurrentWorkspace()
      if self._workspace_name != old_ws_name:
          workspace.SwitchWorkspace(self._workspace_name, True)
  
      op.device_option.CopyFrom(self._device_option)
      if grad_ops is None:
          # TODO(jiayq): use the gradient registration instead of the old
          # hack.
          grad_ops, g_input = getGradientForOp(op)
  
      _input_device_options = (
          input_device_options or core.InferOpBlobDevicesAsDict(op)[0]
      )
      # First, feed in the input.
      for i, arr in enumerate(inputs):
          workspace.FeedBlob(
              op.input[i],
              arr,
              _input_device_options.get(
                  op.input[i], self._device_option
              ),
          )
  
      # Get the loss and gradient for the original.
      grad_name = g_input[input_to_check]
      loss, grad = self.GetLossAndGrad(
          op,
          grad_ops,
          inputs,
          op.input,
          input_to_check,
          grad_name,
          outputs_with_grads,
      )
      grad_estimate = np.zeros_like(inputs[input_to_check])
      if grad_estimate.shape != grad.shape:
          raise Exception(
              "Mismatched gradient shapes: estimated ({}), grad ({})".format(
                  grad_estimate.shape, grad.shape
              )
          )
  
      if ensure_outputs_are_inferred:
          self._assertInferTensorChecks(op, grad_ops)
  
      full_grad_check = os.getenv("CAFFE2_FULL_GRAD_CHECK") == "1"
  
      dims_to_check = inputs[input_to_check].size
      for current_dim in range(dims_to_check):
          # Grad check is very expensive (as it involves running the op from
          # scratch for each of the input tensor elements). Thus, let's
          # run it by default only on a small subset of dimensions. Here we
          # apply very scientific approach: the first and the last 3 elements
          # of each tensor. Pass CAFFE2_FULL_GRAD_CHECK=1 env var to enable
          # the full check
          if (
              not full_grad_check
              and current_dim >= 3
              and current_dim + 3 < dims_to_check
          ):
              grad_estimate.flat[current_dim] = grad.flat[current_dim]
              continue
          # Positive gradient
          inputs[input_to_check].flat[current_dim] += self._stepsize
          pos_loss, _ = self.GetLossAndGrad(
              op,
              grad_ops,
              inputs,
              op.input,
              input_to_check,
              grad_name,
              outputs_with_grads,
          )
          # Negative gradient
          inputs[input_to_check].flat[current_dim] -= self._stepsize * 2
          neg_loss, _ = self.GetLossAndGrad(
              op,
              grad_ops,
              inputs,
              op.input,
              input_to_check,
              grad_name,
              outputs_with_grads,
          )
          # Recover the value
          inputs[input_to_check].flat[current_dim] += self._stepsize
          grad_estimate.flat[current_dim] = (
              (pos_loss - neg_loss) / self._stepsize / 2
          )
      # Now, check correctness
      fail_mat = ~np.isclose(
          grad,
          grad_estimate,
          atol=self._threshold,
          rtol=self._threshold,
      )
      if np.any(fail_mat):
          idx = np.flatnonzero(fail_mat)
          print("Failed. [idx, grad, grad_estimate] are:")
          print(
              np.vstack(
                  [idx, grad.flat[idx], grad_estimate.flat[idx]]
              ).T
          )
          ret = False
      else:
          ret = True
      # After finishing, cleaning up things.
      if self._workspace_name != old_ws_name:
          # We reset the workspace to make sure everything intermediate is
          # cleaned up. Note that there is no need to delete a workspace -
          # when empty it takes a very limited amount of memory.
          workspace.ResetWorkspace()
          workspace.SwitchWorkspace(old_ws_name)
      return ret, grad, grad_estimate
  
file_path: pytorch/caffe2/python/gradient_checker.py
imports: import os
  import numpy
  from caffe2.python import core, workspace, net_drawer
  from caffe2.proto import caffe2_pb2
label_desc: Use image node_id 3 for calling the GradientChecker obj's underlying member method code with example usage: obj.CheckSimple(op, inputs, input_to_check, outputs_with_grads, grad_ops, input_device_options, ensure_outputs_are_inferred) and returns: ret,  grad,  grad_estimate