docstr: A function that simulates trajectories of the (smoothed) Fisher exact test statistic for the desired
  reference set and window sizes under the null distribution, where both the reference set and deployment
  stream follow the same distribution. It then uses these simulated trajectories to estimate thresholds.
  
  The test statistics are smoothed using an exponential moving average to remove their discreteness and
  therefore allow more precise quantiles to be targeted.
  
  In the unsmoothed case the thresholds should stop changing after t=(2*max-window-size - 1) and therefore
  we need only simulate trajectories and estimate thresholds up to this point. If heavy smoothing is applied
  (i.e. if `lam`<<1), a larger `t_max` may be necessary in order to ensure the thresholds have converged.
docstr_tok: ["A","function","that","simulates","trajectories","of","the","(","smoothed",")","Fisher","exact","test","statistic","for","the","desired","reference","set","and","window","sizes","under","the","null","distribution",",","where","both","the","reference","set","and","deployment","stream","follow","the","same","distribution",".","It","then","uses","these","simulated","trajectories","to","estimate","thresholds",".","The","test","statistics","are","smoothed","using","an","exponential","moving","average","to","remove","their","discreteness","and","therefore","allow","more","precise","quantiles","to","be","targeted",".","In","the","unsmoothed","case","the","thresholds","should","stop","changing","after","t=","(","2","*","max-window-size","-","1",")","and","therefore","we","need","only","simulate","trajectories","and","estimate","thresholds","up","to","this","point",".","If","heavy","smoothing","is","applied","(","i.e",".","if","`","lam","`","<","<","1",")",",","a","larger","`","t_max","`","may","be","necessary","in","order","to","ensure","the","thresholds","have","converged","."]
code: def _configure_thresholds(self) -> None:
      """
      A function that simulates trajectories of the (smoothed) Fisher exact test statistic for the desired
      reference set and window sizes under the null distribution, where both the reference set and deployment
      stream follow the same distribution. It then uses these simulated trajectories to estimate thresholds.
  
      The test statistics are smoothed using an exponential moving average to remove their discreteness and
      therefore allow more precise quantiles to be targeted.
  
      In the unsmoothed case the thresholds should stop changing after t=(2*max-window-size - 1) and therefore
      we need only simulate trajectories and estimate thresholds up to this point. If heavy smoothing is applied
      (i.e. if `lam`<<1), a larger `t_max` may be necessary in order to ensure the thresholds have converged.
      """
      if self.verbose:
          print(
              "Using %d bootstrap simulations to configure thresholds..."
              % self.n_bootstraps
          )
  
      # Assuming independent features, calibrate to beta = 1 - (1-FPR)^(1/n_features)
      beta = 1 - (1 - self.fpr) ** (1 / self.n_features)
  
      # Init progress bar
      if self.verbose:
          if self.n_features > 1:
              msg = (
                  "Simulating streams for %d window(s) and %d features(s)"
                  % (len(self.window_sizes), self.n_features)
              )
          else:
              msg = "Simulating streams for %d window(s)" % len(
                  self.window_sizes
              )
          pbar = tqdm(
              total=int(self.n_features * len(self.window_sizes)),
              desc=msg,
          )
      else:
          pbar = None
  
      # Compute test statistic at each t_max number of t's, for each stream and each feature
      self.permit_probs = np.full((self.t_max, self.n_features), np.nan)
      thresholds = np.full(
          (self.t_max, self.n_features), np.nan, dtype=np.float32
      )
      for f in range(self.n_features):
          # Compute stats for given feature (for each stream)
          stats = self._simulate_streams(self.x_ref[:, f], pbar)
          # At each t for each stream, find max stats. over window sizes
          with warnings.catch_warnings():
              warnings.filterwarnings(
                  action="ignore", message="All-NaN slice encountered"
              )
              max_stats = np.nanmax(stats, -1)
          # Find threshold (at each t) that satisfies eqn. (2) in Ross et al.
          for t in range(np.min(self.window_sizes) - 1, self.t_max):
              # Compute (1-beta) quantile of max_stats at a given t, over all streams
              threshold = np.float32(
                  quantile(
                      max_stats[:, t],
                      1 - beta,
                      interpolate=False,
                      type=6,
                  )
              )
              stats_below = max_stats[max_stats[:, t] < threshold]
              # Check for stats equal to threshold
              prob_of_equal = (max_stats[:, t] <= threshold).mean() - (
                  max_stats[:, t] < threshold
              ).mean()
              if prob_of_equal == 0.0:
                  permit_prob = np.inf
                  max_stats = stats_below  # Remove streams where change point detected
              else:
                  undershoot = (
                      1 - beta - (max_stats[:, t] < threshold).mean()
                  )
                  permit_prob = undershoot / prob_of_equal
                  stats_equal = max_stats[max_stats[:, t] == threshold]
                  n_keep_equal = np.random.binomial(
                      len(stats_equal), permit_prob
                  )
                  # Remove streams where change point detected, but allow permit_prob streams where stats=thresh
                  max_stats = np.concatenate(
                      [stats_below, stats_equal[:n_keep_equal]]
                  )
  
              thresholds[t, f] = threshold
              self.permit_probs[t, f] = permit_prob
      self.thresholds = thresholds
  
imports: from tqdm import tqdm
  import numpy
  from typing import Any, Callable, List, Optional, Union
  from alibi_detect.base import DriftConfigMixin
  from alibi_detect.cd.base_online import BaseUniDriftOnline
  from alibi_detect.utils.misc import quantile
  from scipy.stats import hypergeom
  import numba
  import warnings
label_desc: Use image node_id 3 for calling the FETDriftOnline obj's underlying member method code with example usage: obj._configure_thresholds() without return types