class_docstr: Class for all methodologies implemented to analyze clusters and determine whether they are poisonous.
docstr: Analyzes clusters to determine level of suspiciousness of poison based on the cluster's relative size
  and silhouette score.
  Computes a silhouette score for each class to determine how cohesive resulting clusters are.
  A low silhouette score indicates that the clustering does not fit the data well, and the class can be considered
  to be un-poisoned. Conversely, a high silhouette score indicates that the clusters reflect true splits in the
  data.
  The method concludes that a cluster is poison based on the silhouette score and the cluster relative size.
  If the relative size is too small, below a size_threshold and at the same time
  the silhouette score is higher than silhouette_threshold, the cluster is classified as poisonous.
  If the above thresholds are not provided, the default ones will be used.
  
  :param separated_clusters: list where `separated_clusters[i]` is the cluster assignments for the ith class.
  :param reduced_activations_by_class: list where separated_activations[i] is a 1D array of [0,1] for
         [poison,clean].
  :param size_threshold: (optional) threshold used to define when a cluster is substantially smaller. A default
  value is used if the parameter is not provided.
  :param silhouette_threshold: (optional) threshold used to define when a cluster is cohesive. Default
  value is used if the parameter is not provided.
  :param r_size: Round number used for size rate comparisons.
  :param r_silhouette: Round number used for silhouette rate comparisons.
  :return: all_assigned_clean, summary_poison_clusters, report:
           where all_assigned_clean[i] is a 1D boolean array indicating whether a given data point was determined
           to be clean (as opposed to poisonous) summary_poison_clusters: array, where
           summary_poison_clusters[i][j]=1 if cluster j of class j was classified as poison
           report: Dictionary with summary of the analysis.
code: def analyze_by_silhouette_score(
      self,
      separated_clusters: list,
      reduced_activations_by_class: list,
      size_threshold: float = 0.35,
      silhouette_threshold: float = 0.1,
      r_size: int = 2,
      r_silhouette: int = 4,
  ) -> Tuple[np.ndarray, np.ndarray, Dict[str, int]]:
      """
      Analyzes clusters to determine level of suspiciousness of poison based on the cluster's relative size
      and silhouette score.
      Computes a silhouette score for each class to determine how cohesive resulting clusters are.
      A low silhouette score indicates that the clustering does not fit the data well, and the class can be considered
      to be un-poisoned. Conversely, a high silhouette score indicates that the clusters reflect true splits in the
      data.
      The method concludes that a cluster is poison based on the silhouette score and the cluster relative size.
      If the relative size is too small, below a size_threshold and at the same time
      the silhouette score is higher than silhouette_threshold, the cluster is classified as poisonous.
      If the above thresholds are not provided, the default ones will be used.
  
      :param separated_clusters: list where `separated_clusters[i]` is the cluster assignments for the ith class.
      :param reduced_activations_by_class: list where separated_activations[i] is a 1D array of [0,1] for
             [poison,clean].
      :param size_threshold: (optional) threshold used to define when a cluster is substantially smaller. A default
      value is used if the parameter is not provided.
      :param silhouette_threshold: (optional) threshold used to define when a cluster is cohesive. Default
      value is used if the parameter is not provided.
      :param r_size: Round number used for size rate comparisons.
      :param r_silhouette: Round number used for silhouette rate comparisons.
      :return: all_assigned_clean, summary_poison_clusters, report:
               where all_assigned_clean[i] is a 1D boolean array indicating whether a given data point was determined
               to be clean (as opposed to poisonous) summary_poison_clusters: array, where
               summary_poison_clusters[i][j]=1 if cluster j of class j was classified as poison
               report: Dictionary with summary of the analysis.
      """
      # pylint: disable=E0001
      from sklearn.metrics import silhouette_score
  
      size_threshold = round(size_threshold, r_size)
      silhouette_threshold = round(silhouette_threshold, r_silhouette)
      report: Dict[str, Any] = {
          "cluster_analysis": "silhouette_score",
          "size_threshold": str(size_threshold),
          "silhouette_threshold": str(silhouette_threshold),
      }
      all_assigned_clean = []
      nb_classes = len(separated_clusters)
      nb_clusters = len(np.unique(separated_clusters[0]))
      summary_poison_clusters = np.zeros((nb_classes, nb_clusters))
  
      for i, (clusters, activations) in enumerate(
          zip(separated_clusters, reduced_activations_by_class)
      ):
          bins = np.bincount(clusters)
          if np.size(bins) > 2:
              raise ValueError(
                  "Analyzer does not support more than two clusters."
              )
          percentages = np.round(bins / float(np.sum(bins)), r_size)
          poison_clusters = np.where(percentages < size_threshold)
          clean_clusters = np.where(percentages >= size_threshold)
  
          # Generate report for class
          silhouette_avg = round(
              silhouette_score(activations, clusters), r_silhouette
          )
          dict_i: Dict[str, Any] = dict(
              sizes_clusters=str(bins),
              ptc_cluster=str(percentages),
              avg_silhouette_score=str(silhouette_avg),
          )
  
          if np.shape(poison_clusters)[1] != 0:
              # Relative size of the clusters is suspicious
              if silhouette_avg > silhouette_threshold:
                  # In this case the cluster is considered poisonous
                  clean_clusters = np.where(
                      percentages < size_threshold
                  )
                  logger.info(
                      "computed silhouette score: %s", silhouette_avg
                  )
                  dict_i.update(suspicious=True)
              else:
                  poison_clusters = (np.array([[]]),)
                  clean_clusters = np.where(percentages >= 0)
                  dict_i.update(suspicious=False)
          else:
              # If relative size of the clusters is Not suspicious, we conclude it's not suspicious.
              dict_i.update(suspicious=False)
  
          report_class: Dict[str, Dict[str, bool]] = {
              "class_" + str(i): dict_i
          }
  
          for p_id in poison_clusters[0]:
              summary_poison_clusters[i][p_id] = 1
          for c_id in clean_clusters[0]:
              summary_poison_clusters[i][c_id] = 0
  
          assigned_clean = self.assign_class(
              clusters, clean_clusters[0], poison_clusters[0]
          )
          all_assigned_clean.append(assigned_clean)
          report.update(report_class)
  
      return (
          np.asarray(all_assigned_clean, dtype=object),
          summary_poison_clusters,
          report,
      )
  
file_path: adversarial-robustness-toolbox/art/defences/detector/poison/clustering_analyzer.py
imports: from __future__ import absolute_import, division, print_function, unicode_literals
  import logging
  from typing import Any, Dict, List, Tuple
  import numpy
label_desc: Use image node_id 5 for calling the ClusteringAnalyzer obj's underlying member method code with example usage: obj.analyze_by_silhouette_score(separated_clusters, reduced_activations_by_class, size_threshold, silhouette_threshold, r_size, r_silhouette) and returns: np,  summary_poison_clusters,  report