class_docstr: Implementation of the default ART Summary Writer.
class_docstr_tok: ["Implementation","of","the","default","ART","Summary","Writer","."]
docstr: Update the summary writer.
  
  :param batch_id: Id of the current mini-batch.
  :param global_step: Global iteration step.
  :param grad: Loss gradients.
  :param patch: Adversarial patch.
  :param estimator: The estimator to evaluate or calculate gradients of `grad` is None to obtain new metrics.
  :param x: Input data.
  :param y: True or target labels.
  :param targeted: Indicates whether the attack is targeted (True) or untargeted (False).
docstr_tok: ["Update","the","summary","writer",".",":","param","batch_id",":","Id","of","the","current","mini-batch",".",":","param","global_step",":","Global","iteration","step",".",":","param","grad",":","Loss","gradients",".",":","param","patch",":","Adversarial","patch",".",":","param","estimator",":","The","estimator","to","evaluate","or","calculate","gradients","of","`","grad","`","is","None","to","obtain","new","metrics",".",":","param","x",":","Input","data",".",":","param","y",":","True","or","target","labels",".",":","param","targeted",":","Indicates","whether","the","attack","is","targeted","(","True",")","or","untargeted","(","False",")","."]
code: def update(
      self,
      batch_id: int,
      global_step: int,
      grad: Optional[np.ndarray] = None,
      patch: Optional[np.ndarray] = None,
      estimator=None,
      x: Optional[np.ndarray] = None,
      y: Optional[np.ndarray] = None,
      targeted: bool = False,
      **kwargs,
  ):
      """
      Update the summary writer.
  
      :param batch_id: Id of the current mini-batch.
      :param global_step: Global iteration step.
      :param grad: Loss gradients.
      :param patch: Adversarial patch.
      :param estimator: The estimator to evaluate or calculate gradients of `grad` is None to obtain new metrics.
      :param x: Input data.
      :param y: True or target labels.
      :param targeted: Indicates whether the attack is targeted (True) or untargeted (False).
      """
  
      # Gradients
      if grad is not None:
          l_1 = np.linalg.norm(
              grad.reshape(grad.shape[0], -1), axis=1, ord=1
          )
          self.summary_writer.add_scalars(
              f"gradients/norm-L1/batch-{batch_id}",
              {str(i): v for i, v in enumerate(l_1)},
              global_step=global_step,
          )
  
          l_2 = np.linalg.norm(
              grad.reshape(grad.shape[0], -1), axis=1, ord=2
          )
          self.summary_writer.add_scalars(
              f"gradients/norm-L2/batch-{batch_id}",
              {str(i): v for i, v in enumerate(l_2)},
              global_step=global_step,
          )
  
          l_inf = np.linalg.norm(
              grad.reshape(grad.shape[0], -1), axis=1, ord=np.inf
          )
          self.summary_writer.add_scalars(
              f"gradients/norm-Linf/batch-{batch_id}",
              {str(i): v for i, v in enumerate(l_inf)},
              global_step=global_step,
          )
  
      # Patch
      if patch is not None:
          if patch.shape[2] in [1, 3, 4]:
              patch = np.transpose(patch, (2, 0, 1))
          self.summary_writer.add_image(
              "patch",
              patch,
              global_step=global_step,
          )
  
      # Losses
      if estimator is not None and x is not None and y is not None:
          if hasattr(estimator, "compute_losses"):
              losses = estimator.compute_losses(x=x, y=y)
  
              for key, value in losses.items():
                  if np.ndim(value) == 0:
                      self.summary_writer.add_scalar(
                          f"loss/{key}/batch-{batch_id}",
                          value,
                          global_step=global_step,
                      )
                  else:
                      self.summary_writer.add_scalars(
                          f"loss/{key}/batch-{batch_id}",
                          {str(i): v for i, v in enumerate(value)},
                          global_step=global_step,
                      )
  
          elif hasattr(estimator, "compute_loss"):
              loss = estimator.compute_loss(x=x, y=y)
  
              if np.ndim(loss) == 0:
                  self.summary_writer.add_scalar(
                      f"loss/batch-{batch_id}",
                      loss,
                      global_step=global_step,
                  )
              else:
                  self.summary_writer.add_scalars(
                      f"loss/batch-{batch_id}",
                      {str(i): v for i, v in enumerate(loss)},
                      global_step=global_step,
                  )
  
      # Indicators of Attack Failure by Pintor et al. (2021)
      # Paper link: https://arxiv.org/abs/2106.09947
      if self.ind_1:  # Silent Success
          from art.estimators.classification.classifier import (
              ClassifierMixin,
          )
  
          if isinstance(estimator, ClassifierMixin):
              if y is not None:
                  y_pred = estimator.predict(x)  # type: ignore
                  self.i_1 = np.argmax(y_pred, axis=1) == np.argmax(
                      y, axis=1
                  )
                  self.summary_writer.add_scalars(
                      f"Attack Failure Indicator 1 - Silent Success/batch-{batch_id}",
                      {str(i): v for i, v in enumerate(self.i_1)},
                      global_step=global_step,
                  )
              else:
                  raise ValueError(
                      "Attack Failure Indicator 1 requires `y`."
                  )
          else:
              raise ValueError(
                  "Attack Failure Indicator 1 is only supported for classification, for the current "
                  "`estimator` set `ind_1=False`."
              )
  
      if self.ind_2:  # Break-point Angle
          losses = estimator.compute_loss(x=x, y=y)
  
          if str(batch_id) not in self.losses:
              self.losses[str(batch_id)] = []
  
          self.losses[str(batch_id)].append(losses)
  
          self.i_2 = np.ones_like(losses)
  
          if len(self.losses[str(batch_id)]) >= 3:
              delta_loss = (
                  self.losses[str(batch_id)][0]
                  - self.losses[str(batch_id)][-1]
              )
              delta_step = global_step
  
              side_b = sqrt(2.0)
  
              for i_step in range(
                  1, len(self.losses[str(batch_id)]) - 1
              ):
                  side_a = np.sqrt(
                      np.square(
                          (
                              self.losses[str(batch_id)][0]
                              - self.losses[str(batch_id)][i_step]
                          )
                          / delta_loss
                      )
                      + (i_step / delta_step) ** 2
                  )
                  side_c = np.sqrt(
                      np.square(
                          (
                              self.losses[str(batch_id)][i_step]
                              - self.losses[str(batch_id)][-1]
                          )
                          / delta_loss
                      )
                      + ((delta_step - i_step) / delta_step) ** 2
                  )
                  cos_beta = -(
                      side_b**2 - (side_a**2 + side_c**2)
                  ) / (2 * side_a * side_c)
  
                  i_2_step = 1 - np.abs(cos_beta)
                  self.i_2 = np.minimum(self.i_2, i_2_step)
  
              if np.ndim(self.i_2) == 0:
                  self.summary_writer.add_scalar(
                      f"Attack Failure Indicator 2 - Break-point Angle/batch-{batch_id}",
                      self.i_2,
                      global_step=global_step,
                  )
              else:
                  self.summary_writer.add_scalars(
                      f"Attack Failure Indicator 2 - Break-point Angle/batch-{batch_id}",
                      {str(i): v for i, v in enumerate(self.i_2)},
                      global_step=global_step,
                  )
  
      if self.ind_3:  # Diverging (Increasing) Loss
          loss = estimator.compute_loss(x=x, y=y)
  
          if str(batch_id) in self.i_3:
              if targeted:
                  if isinstance(loss, float):
                      loss_add = loss
                  else:
                      loss_add = loss[
                          loss > self.loss_prev[str(batch_id)]
                      ]
                  self.i_3[str(batch_id)][
                      loss > self.loss_prev[str(batch_id)]
                  ] += loss_add
              else:
                  if isinstance(loss, float):
                      loss_add = loss
                  else:
                      loss_add = loss[
                          loss < self.loss_prev[str(batch_id)]
                      ]
                  self.i_3[str(batch_id)][
                      loss < self.loss_prev[str(batch_id)]
                  ] += loss_add
          else:
              self.i_3[str(batch_id)] = np.zeros_like(loss)
  
          if np.ndim(self.i_3[str(batch_id)]) == 0:
              self.summary_writer.add_scalar(
                  f"Attack Failure Indicator 3 - Diverging Loss/batch-{batch_id}",
                  self.i_3[str(batch_id)],
                  global_step=global_step,
              )
          else:
              self.summary_writer.add_scalars(
                  f"Attack Failure Indicator 3 - Diverging Loss/batch-{batch_id}",
                  {
                      str(i): v
                      for i, v in enumerate(self.i_3[str(batch_id)])
                  },
                  global_step=global_step,
              )
  
          self.loss_prev[str(batch_id)] = loss
  
      if self.ind_4:  # Zero Gradients
          threshold = 0.0
  
          if grad is not None:
              if str(batch_id) not in self.i_4:
                  self.i_4[str(batch_id)] = np.zeros(grad.shape[0])
  
              self.i_4[str(batch_id)][
                  np.linalg.norm(
                      grad.reshape(grad.shape[0], -1), axis=1, ord=2
                  )
                  <= threshold
              ] += 1
  
              self.summary_writer.add_scalars(
                  f"Attack Failure Indicator 4 - Zero Gradients/batch-{batch_id}",
                  {
                      str(i): v
                      for i, v in enumerate(
                          self.i_4[str(batch_id)] / global_step
                      )
                  },
                  global_step=global_step,
              )
          else:
              raise ValueError(
                  "Attack Failure Indicator 4 requires `grad`."
              )
  
file_path: adversarial-robustness-toolbox/art/summary_writer.py
imports: from abc import ABC, abstractmethod
  from math import sqrt
  from typing import Dict, List, Optional, Union
  import numpy
label_desc: Use image node_id 2 for calling the SummaryWriterDefault obj's underlying member method code with example usage: obj.update(batch_id, global_step, grad, patch, estimator, x, y, targeted) without return types