docstr: Calculates the attributions for each input feature or element of layer and
  returns an Explanation object.
  
  Parameters
  ----------
  X
      Instance for which integrated gradients attribution are computed.
  forward_kwargs
      Input keyword args. If it's not ``None``, it must be a dict with `numpy` arrays as values.
      The first dimension of the arrays must correspond to the number of examples.
      It will be repeated for each of `n_steps` along the integrated path.
      The attributions are not computed with respect to these arguments.
  baselines
      Baselines (starting point of the path integral) for each instance.
      If the passed value is an `np.ndarray` must have the same shape as `X`.
      If not provided, all features values for the baselines are set to 0.
  target
      Defines which element of the model output is considered to compute the gradients.
      Target can be a numpy array, a list or a numeric value.
      Numeric values are only valid if the model's output is a rank-n tensor
      with n <= 2 (regression and classification models).
      If a numeric value is passed, the gradients are calculated for
      the same element of the output for all data points.
      For regression models whose output is a scalar, target should not be provided.
      For classification models `target` can be either the true classes or the classes predicted by the model.
      It must be provided for classification models and regression models whose output is a vector.
      If the model's output is a rank-n tensor with n > 2,
      the target must be a rank-2 numpy array or a list of lists (a matrix) with dimensions nb_samples X (n-1) .
  attribute_to_layer_inputs
      In case of layers gradients, controls whether the gradients are computed for the layer's inputs or
      outputs. If ``True``, gradients are computed for the layer's inputs, if ``False`` for the layer's outputs.
  
  Returns
  -------
  explanation
      `Explanation` object including `meta` and `data` attributes with integrated gradients attributions
      for each feature. See usage at `IG examples`_ for details.
  
      .. _IG examples:
          https://docs.seldon.io/projects/alibi/en/stable/methods/IntegratedGradients.html
docstr_tok: ["Calculates","the","attributions","for","each","input","feature","or","element","of","layer","and","returns","an","Explanation","object",".","Parameters","--","--","--","--","--","X","Instance","for","which","integrated","gradients","attribution","are","computed",".","forward_kwargs","Input","keyword","args",".","If","it","'s","not","``","None","``",",","it","must","be","a","dict","with","`","numpy","`","arrays","as","values",".","The","first","dimension","of","the","arrays","must","correspond","to","the","number","of","examples",".","It","will","be","repeated","for","each","of","`","n_steps","`","along","the","integrated","path",".","The","attributions","are","not","computed","with","respect","to","these","arguments",".","baselines","Baselines","(","starting","point","of","the","path","integral",")","for","each","instance",".","If","the","passed","value","is","an","`","np.ndarray","`","must","have","the","same","shape","as","`","X","`",".","If","not","provided",",","all","features","values","for","the","baselines","are","set","to","0.","target","Defines","which","element","of","the","model","output","is","considered","to","compute","the","gradients",".","Target","can","be","a","numpy","array",",","a","list","or","a","numeric","value",".","Numeric","values","are","only","valid","if","the","model","'s","output","is","a","rank-n","tensor","with","n","<","=","2","(","regression","and","classification","models",")",".","If","a","numeric","value","is","passed",",","the","gradients","are","calculated","for","the","same","element","of","the","output","for","all","data","points",".","For","regression","models","whose","output","is","a","scalar",",","target","should","not","be","provided",".","For","classification","models","`","target","`","can","be","either","the","true","classes","or","the","classes","predicted","by","the","model",".","It","must","be","provided","for","classification","models","and","regression","models","whose","output","is","a","vector",".","If","the","model","'s","output","is","a","rank-n","tensor","with","n",">","2",",","the","target","must","be","a","rank-2","numpy","array","or","a","list","of","lists","(","a","matrix",")","with","dimensions","nb_samples","X","(","n-1",")",".","attribute_to_layer_inputs","In","case","of","layers","gradients",",","controls","whether","the","gradients","are","computed","for","the","layer","'s","inputs","or","outputs",".","If","``","True","``",",","gradients","are","computed","for","the","layer","'s","inputs",",","if","``","False","``","for","the","layer","'s","outputs",".","Returns","--","--","--","-","explanation","`","Explanation","`","object","including","`","meta","`","and","`","data","`","attributes","with","integrated","gradients","attributions","for","each","feature",".","See","usage","at","`","IG","examples","`","_","for","details",".","..","_IG","examples",":","https",":","\/\/docs.seldon.io\/projects\/alibi\/en\/stable\/methods\/IntegratedGradients.html"]
code: def explain(
      self,
      X: Union[np.ndarray, List[np.ndarray]],
      forward_kwargs: Optional[dict] = None,
      baselines: Optional[
          Union[
              int,
              float,
              np.ndarray,
              List[int],
              List[float],
              List[np.ndarray],
          ]
      ] = None,
      target: Optional[Union[int, list, np.ndarray]] = None,
      attribute_to_layer_inputs: bool = False,
  ) -> Explanation:
      """Calculates the attributions for each input feature or element of layer and
      returns an Explanation object.
  
      Parameters
      ----------
      X
          Instance for which integrated gradients attribution are computed.
      forward_kwargs
          Input keyword args. If it's not ``None``, it must be a dict with `numpy` arrays as values.
          The first dimension of the arrays must correspond to the number of examples.
          It will be repeated for each of `n_steps` along the integrated path.
          The attributions are not computed with respect to these arguments.
      baselines
          Baselines (starting point of the path integral) for each instance.
          If the passed value is an `np.ndarray` must have the same shape as `X`.
          If not provided, all features values for the baselines are set to 0.
      target
          Defines which element of the model output is considered to compute the gradients.
          Target can be a numpy array, a list or a numeric value.
          Numeric values are only valid if the model's output is a rank-n tensor
          with n <= 2 (regression and classification models).
          If a numeric value is passed, the gradients are calculated for
          the same element of the output for all data points.
          For regression models whose output is a scalar, target should not be provided.
          For classification models `target` can be either the true classes or the classes predicted by the model.
          It must be provided for classification models and regression models whose output is a vector.
          If the model's output is a rank-n tensor with n > 2,
          the target must be a rank-2 numpy array or a list of lists (a matrix) with dimensions nb_samples X (n-1) .
      attribute_to_layer_inputs
          In case of layers gradients, controls whether the gradients are computed for the layer's inputs or
          outputs. If ``True``, gradients are computed for the layer's inputs, if ``False`` for the layer's outputs.
  
      Returns
      -------
      explanation
          `Explanation` object including `meta` and `data` attributes with integrated gradients attributions
          for each feature. See usage at `IG examples`_ for details.
  
          .. _IG examples:
              https://docs.seldon.io/projects/alibi/en/stable/methods/IntegratedGradients.html
      """
      # target handling logic
      if self.target_fn and target is not None:
          msg = "Both `target_fn` and `target` were provided. Only one of these should be provided."
          raise ValueError(msg)
      if self.target_fn:
          target = _get_target_from_target_fn(
              self.target_fn, self.model, X, forward_kwargs
          )
  
      self._is_list = isinstance(X, list)
      self._is_np = isinstance(X, np.ndarray)
      if forward_kwargs is None:
          forward_kwargs = {}
  
      if self._is_list:
          X = cast(List[np.ndarray], X)  # help mypy out
          self.orig_dummy_input = [
              np.zeros((1,) + xx.shape[1:], dtype=xx.dtype) for xx in X
          ]
          nb_samples = len(X[0])
          input_dtypes = [xx.dtype for xx in X]
          # Formatting baselines in case of models with multiple inputs
          if baselines is None:
              baselines = [None for _ in range(len(X))]  # type: ignore
          else:
              if not isinstance(baselines, list):
                  raise ValueError(
                      f"If the input X is a list, baseline can only be `None` or "
                      f"a list of the same length of X. Found baselines type {type(baselines)}"
                  )
              else:
                  if len(X) != len(baselines):
                      raise ValueError(
                          f"Length of 'X' must match length of 'baselines'. "
                          f"Found len(X): {len(X)}, len(baselines): {len(baselines)}"
                      )
  
          if max([len(x) for x in X]) != min([len(x) for x in X]):
              raise ValueError(
                  "First dimension must be egual for all inputs"
              )
  
          for i in range(len(X)):
              x, baseline = X[i], baselines[i]  # type: ignore
              # format and check baselines
              baseline = _format_baseline(x, baseline)
              baselines[i] = baseline  # type: ignore
  
      elif self._is_np:
          X = cast(np.ndarray, X)  # help mypy out
          self.orig_dummy_input = np.zeros(
              (1,) + X.shape[1:], dtype=X.dtype
          )
          nb_samples = len(X)
          input_dtypes = [X.dtype]
          # Formatting baselines for models with a single input
          baselines = _format_baseline(X, baselines)  # type: ignore # TODO: validate/narrow baselines type
  
      else:
          raise ValueError(
              "Input must be a np.ndarray or a list of np.ndarray"
          )
  
      # defining integral method
      step_sizes_func, alphas_func = approximation_parameters(
          self.method
      )
      step_sizes, alphas = step_sizes_func(self.n_steps), alphas_func(
          self.n_steps
      )
      target = _format_target(target, nb_samples)
  
      if self._is_list:
          X = cast(List[np.ndarray], X)  # help mypy out
          # Attributions calculation in case of multiple inputs
          if not self._has_inputs:
              # Inferring model's inputs from data points for models with no explicit inputs
              # (typically subclassed models)
              inputs = [
                  tf.keras.Input(shape=xx.shape[1:], dtype=xx.dtype)
                  for xx in X
              ]
              self.model(inputs, **forward_kwargs)
  
          _validate_output(self.model, target)
          _check_target(self.model.output_shape, target, nb_samples)
          if self.layer is None:
              # No layer passed, attributions computed with respect to the inputs
              attributions = self._compute_attributions_list_input(
                  X,
                  baselines,  # type: ignore[arg-type]
                  target,
                  step_sizes,
                  alphas,
                  nb_samples,
                  forward_kwargs,
                  attribute_to_layer_inputs,
              )
  
          else:
              # forwad inputs and  baselines
              X_layer, baselines_layer = _forward_input_baseline(
                  X,
                  baselines,  # type: ignore[arg-type]
                  self.model,
                  self.layer,
                  self.orig_call,  # type: ignore[arg-type]
                  forward_kwargs=forward_kwargs,
                  forward_to_inputs=attribute_to_layer_inputs,
              )
  
              if isinstance(X_layer, list) and isinstance(
                  baselines_layer, list
              ):
                  attributions = self._compute_attributions_list_input(
                      X_layer,
                      baselines_layer,
                      target,
                      step_sizes,
                      alphas,
                      nb_samples,
                      forward_kwargs,
                      attribute_to_layer_inputs,
                  )
              else:
                  attributions = (
                      self._compute_attributions_tensor_input(
                          X_layer,
                          baselines_layer,
                          target,
                          step_sizes,
                          alphas,
                          nb_samples,
                          forward_kwargs,
                          attribute_to_layer_inputs,
                      )
                  )
  
      else:
          # Attributions calculation in case of single input
          if not self._has_inputs:
              inputs = tf.keras.Input(shape=X.shape[1:], dtype=X.dtype)  # type: ignore
              self.model(inputs, **forward_kwargs)
  
          _validate_output(self.model, target)
          _check_target(self.model.output_shape, target, nb_samples)
          if self.layer is None:
              attributions = self._compute_attributions_tensor_input(
                  X,
                  baselines,
                  target,
                  step_sizes,
                  alphas,
                  nb_samples,
                  forward_kwargs,
                  attribute_to_layer_inputs,
              )
  
          else:
              # forwad inputs and  baselines
              X_layer, baselines_layer = _forward_input_baseline(
                  X,
                  baselines,  # type: ignore[arg-type]
                  self.model,
                  self.layer,
                  self.orig_call,  # type: ignore[arg-type]
                  forward_kwargs=forward_kwargs,
                  forward_to_inputs=attribute_to_layer_inputs,
              )
  
              if isinstance(X_layer, list) and isinstance(
                  baselines_layer, list
              ):
                  attributions = self._compute_attributions_list_input(
                      X_layer,
                      baselines_layer,
                      target,
                      step_sizes,
                      alphas,
                      nb_samples,
                      forward_kwargs,
                      attribute_to_layer_inputs,
                  )
              else:
                  attributions = (
                      self._compute_attributions_tensor_input(
                          X_layer,
                          baselines_layer,
                          target,
                          step_sizes,
                          alphas,
                          nb_samples,
                          forward_kwargs,
                          attribute_to_layer_inputs,
                      )
                  )
      # calculate convergence deltas
      deltas = _compute_convergence_delta(
          self.model,
          input_dtypes,
          attributions,
          baselines,  # type: ignore[arg-type]
          X,
          forward_kwargs,
          target,
          self._is_list,
      )
  
      return self._build_explanation(
          X=X,
          forward_kwargs=forward_kwargs,
          baselines=baselines,  # type: ignore[arg-type]
          target=target,
          attributions=attributions,
          deltas=deltas,
      )
  
imports: import copy
  import logging
  import string
  import warnings
  from enum import Enum
  from typing import Callable, List, Optional, Tuple, Union, cast
  import numpy
  import tensorflow
  from alibi.api.defaults import DEFAULT_DATA_INTGRAD, DEFAULT_META_INTGRAD
  from alibi.api.interfaces import Explainer, Explanation
  from alibi.utils.approximation_methods import approximation_parameters
label_desc: Use image node_id 2 for calling the IntegratedGradients obj's underlying member method code with example usage: obj.explain(X, forward_kwargs, baselines, target, attribute_to_layer_inputs) and returns: self