code: def generate_tf_explanation(
      model, inputs: np.array, targets: np.array, **kwargs
  ) -> np.ndarray:
      """
      Generate explanation for a tf model with tf_explain.
      Assumption: Currently only normalised absolute values of explanations supported.
  
      Parameters
      ----------
      model: tf.keras.Model
              A model that is used for explanation.
      inputs: np.ndarray
           The inputs that ought to be explained.
      targets: np.ndarray
           The target lables that should be used in the explanation.
      kwargs: optional
              Keyword arguments. Pass as "explain_func_kwargs" dictionary when working with a metric class.
              Pass as regular kwargs when using the stnad-alone function.
  
              method: string, optional
                  XAI method.
              xai_lib_kwargs: dictionary, optional
                  Keyword arguments to be passed to the attribution function.
              softmax: boolean, optional
                  Indicated whether softmax activation in the last layer shall be removed.
              channel_first: boolean, optional
                  Indicates if the image dimensions are channel first, or channel last.
                  Inferred from the input shape if None.
              reduce_axes: tuple
                  Indicates the indices of dimensions of the output explanation array to be summed. For example, an input
                  array of shape (8, 28, 28, 3) with keepdims=True and reduce_axes = (-1,) will return an array of shape
                  (8, 28, 28, -1). Passing "()" will keep
                  the original dimensions.
              keepdims: boolean
                  Indicated if the reduced axes shall be preserved (True) or removed (False).
      Returns
      -------
      explanation: np.ndarray
           Returns np.ndarray of same shape as inputs.
  
      """
  
      warnings.warn(
          f"tf-explain library is being used for XAI attribution calculation. Consult with tf-explain documentation "
          f"to find out the type of operations applied on the method output, i.e.  normalisation, taking the absolute"
          f"value, etc.\n",
          category=UserWarning,
      )
  
      method = kwargs.get("method", "VanillaGradients")
      xai_lib_kwargs = kwargs.get("xai_lib_kwargs", {})
      reduce_axes = kwargs.get("reduce_axes", ())
      keepdims = kwargs.get("keepdims", True)
      softmax = kwargs.get("softmax", None)
      if softmax is not None:
          if method == "VanillaGradients":
              warnings.warn(
                  f"tf-explain VanillaGradients method only supports the following architecture:"
                  f"a layer which computes class scores with no activation, "
                  f"followed by an activation layer. Softmax argument is set to True.\n",
                  category=UserWarning,
              )
              softmax = True
          wrapped_model = get_wrapped_model(
              model, softmax=softmax, channel_first=False
          )
          model = wrapped_model.get_softmax_arg_model()
  
      inputs = inputs.reshape(-1, *model.input_shape[1:])
      if not isinstance(targets, np.ndarray):
          targets = np.array([targets])
  
      channel_first = (
          kwargs["channel_first"]
          if "channel_first" in kwargs
          else infer_channel_first(inputs)
      )
  
      inputs = make_channel_last(inputs, channel_first)
  
      explanation: np.ndarray = np.zeros_like(inputs)
  
      if method in constants.DEPRECATED_XAI_METHODS_TF:
          warnings.warn(
              f"Explanation method string {method} is deprecated. Use "
              f"{constants.DEPRECATED_XAI_METHODS_TF[method]} instead.\n",
              category=UserWarning,
          )
          method = constants.DEPRECATED_XAI_METHODS_TF[method]
  
      if method == "VanillaGradients":
          explainer = (
              tf_explain.core.vanilla_gradients.VanillaGradients()
          )
          explanation = (
              np.array(
                  list(
                      map(
                          lambda x, y: explainer.explain(
                              ([x], None), model, y, **xai_lib_kwargs
                          ),
                          inputs,
                          targets,
                      )
                  ),
                  dtype=float,
              )
              / 255
          )
  
      elif method == "IntegratedGradients":
          n_steps = kwargs.get("n_steps", 10)
          explainer = (
              tf_explain.core.integrated_gradients.IntegratedGradients()
          )
          explanation = (
              np.array(
                  list(
                      map(
                          lambda x, y: explainer.explain(
                              ([x], None),
                              model,
                              y,
                              n_steps=n_steps,
                              **xai_lib_kwargs,
                          ),
                          inputs,
                          targets,
                      )
                  ),
                  dtype=float,
              )
              / 255
          )
  
      elif method == "GradientsInput":
          explainer = tf_explain.core.gradients_inputs.GradientsInputs()
          explanation = (
              np.array(
                  list(
                      map(
                          lambda x, y: explainer.explain(
                              ([x], None), model, y, **xai_lib_kwargs
                          ),
                          inputs,
                          targets,
                      )
                  ),
                  dtype=float,
              )
              / 255
          )
  
      elif method == "OcclusionSensitivity":
          patch_size = kwargs.get(
              "window", (1, *([4] * (inputs.ndim - 2)))
          )[-1]
          reduce_axes = kwargs.get("reduce_axes", (-1,))
          keepdims = kwargs.get("keepdims", False)
          keep_dim = False
          explainer = (
              tf_explain.core.occlusion_sensitivity.OcclusionSensitivity()
          )
          explanation = (
              np.array(
                  list(
                      map(
                          lambda x, y: explainer.explain(
                              ([x], None),
                              model,
                              y,
                              patch_size=patch_size,
                              **xai_lib_kwargs,
                          ),
                          inputs,
                          targets,
                      )
                  ),
                  dtype=float,
              )
              / 255
          )
  
      elif method == "GradCAM":
          reduce_axes = kwargs.get("reduce_axes", (-1,))
          keepdims = kwargs.get("keepdims", False)
          keep_dim = False
          if "gc_layer" in kwargs:
              xai_lib_kwargs["layer_name"] = kwargs["gc_layer"]
  
          explainer = tf_explain.core.grad_cam.GradCAM()
          explanation = (
              np.array(
                  list(
                      map(
                          lambda x, y: explainer.explain(
                              ([x], None), model, y, **xai_lib_kwargs
                          ),
                          inputs,
                          targets,
                      )
                  ),
                  dtype=float,
              )
              / 255
          )
  
      elif method == "SmoothGrad":
          num_samples = kwargs.get("num_samples", 5)
          noise = kwargs.get("noise", 0.1)
          explainer = tf_explain.core.smoothgrad.SmoothGrad()
          explanation = (
              np.array(
                  list(
                      map(
                          lambda x, y: explainer.explain(
                              ([x], None),
                              model,
                              y,
                              num_samples=num_samples,
                              noise=noise,
                              **xai_lib_kwargs,
                          ),
                          inputs,
                          targets,
                      )
                  ),
                  dtype=float,
              )
              / 255
          )
  
      else:
          raise KeyError(
              f"To use the 'quantus.explain' method with tf-explain as a supporting library, "
              f"specify a XAI method that is supported {constants.AVAILABLE_XAI_METHODS_TF}."
          )
  
      assert 0 not in reduce_axes, (
          "Reduction over batch_axis is not available, please do not "
          "include axis 0 in 'reduce_axes' kwargs."
      )
      assert np.all(np.array(reduce_axes) <= explanation.ndim - 1), (
          "Cannot reduce attributions over more axes than each sample has dimensions, but got "
          "{} and  {}.".format(len(reduce_axes), inputs.ndim - 1)
      )
  
      reduce_axes = {"axis": tuple(reduce_axes), "keepdims": keepdims}
  
      # Prevent attribution summation for 2D-data.
      # Recreate np.sum behavior when passing reduce_axes=(), i.e. no change.
      if (len(tuple(reduce_axes)) == 0) | (explanation.ndim < 3):
          return explanation
  
      return explanation.sum(**reduce_axes)
  
file_path: Quantus/quantus/functions/explanation_func.py
imports: import warnings
  from importlib import util
  from typing import Optional, Union
  import numpy
  import quantus
  import scipy
  from quantus.helpers import constants
  from quantus.helpers import __EXTRAS__
  from quantus.helpers.model.model_interface import ModelInterface
  from quantus.helpers.utils import get_baseline_value, infer_channel_first, make_channel_last, get_wrapped_model
label_desc: Use image node_id 3 for calling a global function with example usage: generate_tf_explanation(model, inputs, targets) and returns: explanation, explanation