class_docstr: Fast generalized subset scan
  
  | Paper link: https://www.cs.cmu.edu/~neill/papers/mcfowland13a.pdf
class_docstr_tok: ["Fast","generalized","subset","scan","|","Paper","link",":","https",":","\/\/www.cs.cmu.edu\/~neill\/papers\/mcfowland13a.pdf"]
docstr: Finds the highest scoring subset of records and attribute. Return the subsets, the score, and the alpha that
  maximizes the score iterates between images and nodes, each time performing NPSS efficient maximization.
  
  :param pvalues: pvalue ranges.
  :param a_max: alpha threshold
  :param restarts: number of iterative restarts
  :param image_to_node_init: intializes what direction to begin the search: image to node or vice-versa
  :param score_function: scoring function
  :return: (best_score, image_sub, node_sub, optimal_alpha)
docstr_tok: ["Finds","the","highest","scoring","subset","of","records","and","attribute",".","Return","the","subsets",",","the","score",",","and","the","alpha","that","maximizes","the","score","iterates","between","images","and","nodes",",","each","time","performing","NPSS","efficient","maximization",".",":","param","pvalues",":","pvalue","ranges",".",":","param","a_max",":","alpha","threshold",":","param","restarts",":","number","of","iterative","restarts",":","param","image_to_node_init",":","intializes","what","direction","to","begin","the","search",":","image","to","node","or","vice-versa",":","param","score_function",":","scoring","function",":","return",":","(","best_score",",","image_sub",",","node_sub",",","optimal_alpha",")"]
code: def fgss_for_nets(
      pvalues: np.ndarray,
      a_max: float = 0.5,
      restarts: int = 10,
      image_to_node_init: bool = False,
      score_function: Callable[
          [np.ndarray, np.ndarray, np.ndarray], np.ndarray
      ] = ScoringFunctions.get_score_bj_fast,
  ) -> Tuple[float, np.ndarray, np.ndarray, float]:
      """
      Finds the highest scoring subset of records and attribute. Return the subsets, the score, and the alpha that
      maximizes the score iterates between images and nodes, each time performing NPSS efficient maximization.
  
      :param pvalues: pvalue ranges.
      :param a_max: alpha threshold
      :param restarts: number of iterative restarts
      :param image_to_node_init: intializes what direction to begin the search: image to node or vice-versa
      :param score_function: scoring function
      :return: (best_score, image_sub, node_sub, optimal_alpha)
      """
      best_score = -100000.0
  
      if len(pvalues) < restarts:
          restarts = len(pvalues)
  
      for r_indx in range(
          0, restarts
      ):  # do random restarts to come close to global maximum
          image_to_node = image_to_node_init
          if r_indx == 0:
              if image_to_node:
                  # all 1's for number of rows
                  indices_of_seeds = np.arange(pvalues.shape[0])
              else:
                  # all 1's for number of cols
                  indices_of_seeds = np.arange(pvalues.shape[1])
  
              (
                  best_score_from_restart,
                  best_image_sub_from_restart,
                  best_node_sub_from_restart,
                  best_alpha_from_restart,
              ) = ScanningOps.single_restart(
                  pvalues,
                  a_max,
                  indices_of_seeds,
                  image_to_node,
                  score_function,
              )
  
              if best_score_from_restart > best_score:
                  best_score = best_score_from_restart
                  image_sub = best_image_sub_from_restart
                  node_sub = best_node_sub_from_restart
                  optimal_alpha = best_alpha_from_restart
  
              # Finished A Restart
          else:
              # New Restart
              # some some randomizing and only leave in a random number of rows of pvalues TODO
              prob = np.random.uniform(0, 1)
              if image_to_node:
                  indices_of_seeds = np.random.choice(
                      np.arange(pvalues.shape[0]),
                      int(pvalues.shape[0] * prob),
                      replace=False,
                  )
              else:
                  indices_of_seeds = np.random.choice(
                      np.arange(pvalues.shape[1]),
                      int(pvalues.shape[1] * prob),
                      replace=False,
                  )
              while indices_of_seeds.size == 0:
                  # eventually will make non zero
                  prob = np.random.uniform(0, 1)
                  if image_to_node:
                      indices_of_seeds = np.random.choice(
                          np.arange(pvalues.shape[0]),
                          int(pvalues.shape[0] * prob),
                          replace=False,
                      )
                  else:
                      indices_of_seeds = np.random.choice(
                          np.arange(pvalues.shape[1]),
                          int(pvalues.shape[1] * prob),
                          replace=False,
                      )
  
              indices_of_seeds.astype(int)
              # process a random subset of rows of pvalues array
              (
                  best_score_from_restart,
                  best_image_sub_from_restart,
                  best_node_sub_from_restart,
                  best_alpha_from_restart,
              ) = ScanningOps.single_restart(
                  pvalues,
                  a_max,
                  indices_of_seeds,
                  image_to_node,
                  score_function,
              )
  
              if best_score_from_restart > best_score:
                  best_score = best_score_from_restart
                  image_sub = best_image_sub_from_restart
                  node_sub = best_node_sub_from_restart
                  optimal_alpha = best_alpha_from_restart
  
              # Finished A Restart
  
      return best_score, image_sub, node_sub, optimal_alpha
  
imports: from typing import Callable, Tuple
  import numpy
  from art.defences.detector.evasion.subsetscanning.scoring_functions import ScoringFunctions
  from art.defences.detector.evasion.subsetscanning.scanningops import ScanningOps
label_desc: Use image node_id 2 for calling the Scanner obj's underlying member method code with example usage: obj.fgss_for_nets(pvalues, a_max, restarts, image_to_node_init, score_function) and returns: best_score,  image_sub,  node_sub,  optimal_alpha