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.
  
  A simplified, faster, exact method but only useable when scoring an individual input. This method recognizes
  that for an individual input, the priority function for a fixed alpha threshold results in all nodes having
  either priority 1 or 0. That is, the pmax is either below the threshold or not. Due to convexity of the scoring
  function we know elements with tied priority are either all included or all excluded. Therefore, each alpha
  threshold uniquely defines a single subset of nodes to be scored. These are the nodes that have pmax less than
  threshold. This means the individual-input scanner is equivalent to sorting pmax values and iteratively adding
  the next largest pmax. There are at most O(N) of these subsets to consider. Sorting requires O(N ln N). There is
  no iterative ascent required and no special choice of alpha thresholds for speed improvements.
  
  :param pvalues: pvalue ranges.
  :param a_max: alpha max. determines the significance level threshold
  :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",".","A","simplified",",","faster",",","exact","method","but","only","useable","when","scoring","an","individual","input",".","This","method","recognizes","that","for","an","individual","input",",","the","priority","function","for","a","fixed","alpha","threshold","results","in","all","nodes","having","either","priority","1","or","0",".","That","is",",","the","pmax","is","either","below","the","threshold","or","not",".","Due","to","convexity","of","the","scoring","function","we","know","elements","with","tied","priority","are","either","all","included","or","all","excluded",".","Therefore",",","each","alpha","threshold","uniquely","defines","a","single","subset","of","nodes","to","be","scored",".","These","are","the","nodes","that","have","pmax","less","than","threshold",".","This","means","the","individual-input","scanner","is","equivalent","to","sorting","pmax","values","and","iteratively","adding","the","next","largest","pmax",".","There","are","at","most","O","(","N",")","of","these","subsets","to","consider",".","Sorting","requires","O","(","N","ln","N",")",".","There","is","no","iterative","ascent","required","and","no","special","choice","of","alpha","thresholds","for","speed","improvements",".",":","param","pvalues",":","pvalue","ranges",".",":","param","a_max",":","alpha","max",".","determines","the","significance","level","threshold",":","param","score_function",":","scoring","function",":","return",":","(","best_score",",","image_sub",",","node_sub",",","optimal_alpha",")"]
code: def fgss_individ_for_nets(
      pvalues: np.ndarray,
      a_max: float = 0.5,
      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.
  
      A simplified, faster, exact method but only useable when scoring an individual input. This method recognizes
      that for an individual input, the priority function for a fixed alpha threshold results in all nodes having
      either priority 1 or 0. That is, the pmax is either below the threshold or not. Due to convexity of the scoring
      function we know elements with tied priority are either all included or all excluded. Therefore, each alpha
      threshold uniquely defines a single subset of nodes to be scored. These are the nodes that have pmax less than
      threshold. This means the individual-input scanner is equivalent to sorting pmax values and iteratively adding
      the next largest pmax. There are at most O(N) of these subsets to consider. Sorting requires O(N ln N). There is
      no iterative ascent required and no special choice of alpha thresholds for speed improvements.
  
      :param pvalues: pvalue ranges.
      :param a_max: alpha max. determines the significance level threshold
      :param score_function: scoring function
      :return: (best_score, image_sub, node_sub, optimal_alpha)
      """
      pmaxes = np.reshape(
          pvalues[:, 1], pvalues.shape[0]
      )  # should be number of columns/nodes
      # we can ignore any pmax that is greater than a_max; this makes sorting faster
      # all the pvalues less than equal a_max are kept by nonzero result of the comparison
      potential_thresholds = pmaxes[np.flatnonzero(pmaxes <= a_max)]
  
      # sorrted_unique provides our alpha thresholds that we will scan
      # count_unique (in cumulative format) will provide the number of observations less than corresponding alpha
      sorted_unique, count_unique = np.unique(
          potential_thresholds, return_counts=True
      )
  
      cumulative_count = np.cumsum(count_unique)
      # In individual input case we have n_a = N, so cumulative count is used for both.
      # sorted_unique provides the increasing alpha values that need to be checked.
      vector_of_scores = score_function(
          cumulative_count, cumulative_count, sorted_unique
      )
  
      # scoring completed, now grab the max (and index)
      best_score_idx = np.argmax(vector_of_scores)
      best_score = vector_of_scores[best_score_idx]
      optimal_alpha = sorted_unique[best_score_idx]
      # best_size = cumulative_count[best_score_idx]
  
      # In order to determine which nodes are included, we look for all pmaxes less than or equal best alpha
      node_sub = np.flatnonzero(pvalues[:, 1] <= optimal_alpha)
      # in the individual input case there's only 1 possible subset of inputs to return - a 1x1 with index 0
      image_sub = np.array([0])
  
      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 1 for calling the Scanner obj's underlying member method code with example usage: obj.fgss_individ_for_nets(pvalues, a_max, score_function) and returns: best_score,  image_sub,  node_sub,  optimal_alpha