docstr: function to register finished runs
  
  Every time a run has finished, this function should be called
  to register it with the result logger. If overwritten, make
  sure to call this method from the base class to ensure proper
  logging.
  
  
  Parameters:
  -----------
  job: hpbandster.distributed.dispatcher.Job object
          contains all the info about the run
docstr_tok: ["function","to","register","finished","runs","Every","time","a","run","has","finished",",","this","function","should","be","called","to","register","it","with","the","result","logger",".","If","overwritten",",","make","sure","to","call","this","method","from","the","base","class","to","ensure","proper","logging",".","Parameters",":","--","--","--","--","--","-","job",":","hpbandster.distributed.dispatcher.Job","object","contains","all","the","info","about","the","run"]
code: def new_result(self, job, update_model=True):
      """
      function to register finished runs
  
      Every time a run has finished, this function should be called
      to register it with the result logger. If overwritten, make
      sure to call this method from the base class to ensure proper
      logging.
  
  
      Parameters:
      -----------
      job: hpbandster.distributed.dispatcher.Job object
              contains all the info about the run
      """
  
      super().new_result(job)
  
      if job.result is None:
          # One could skip crashed results, but we decided to
          # assign a +inf loss and count them as bad configurations
          loss = np.inf
      else:
          # same for non numeric losses.
          # Note that this means losses of minus infinity will count as bad!
          loss = (
              job.result["loss"]
              if np.isfinite(job.result["loss"])
              else np.inf
          )
  
      budget = job.kwargs["budget"]
  
      if budget not in self.configs.keys():
          self.configs[budget] = []
          self.losses[budget] = []
  
      # skip model building if we already have a bigger model
      if max(list(self.kde_models.keys()) + [-np.inf]) > budget:
          return
  
      # We want to get a numerical representation of the configuration in the original space
  
      conf = ConfigSpace.Configuration(
          self.configspace, job.kwargs["config"]
      )
      self.configs[budget].append(conf.get_array())
      self.losses[budget].append(loss)
  
      # skip model building:
      # 		a) if not enough points are available
      if len(self.configs[budget]) <= self.min_points_in_model - 1:
          self.logger.debug(
              "Only %i run(s) for budget %f available, need more than %s -> can't build model!"
              % (
                  len(self.configs[budget]),
                  budget,
                  self.min_points_in_model + 1,
              )
          )
          return
  
      # 		b) during warnm starting when we feed previous results in and only update once
      if not update_model:
          return
  
      train_configs = np.array(self.configs[budget])
      train_losses = np.array(self.losses[budget])
  
      n_good = max(
          self.min_points_in_model,
          (self.top_n_percent * train_configs.shape[0]) // 100,
      )
      # n_bad = min(max(self.min_points_in_model, ((100-self.top_n_percent)*train_configs.shape[0])//100), 10)
      n_bad = max(
          self.min_points_in_model,
          ((100 - self.top_n_percent) * train_configs.shape[0]) // 100,
      )
  
      # Refit KDE for the current budget
      idx = np.argsort(train_losses)
  
      train_data_good = self.impute_conditional_data(
          train_configs[idx[:n_good]]
      )
      train_data_bad = self.impute_conditional_data(
          train_configs[idx[n_good : n_good + n_bad]]
      )
  
      if train_data_good.shape[0] <= train_data_good.shape[1]:
          return
      if train_data_bad.shape[0] <= train_data_bad.shape[1]:
          return
  
      # more expensive crossvalidation method
      # bw_estimation = 'cv_ls'
  
      # quick rule of thumb
      bw_estimation = "normal_reference"
  
      bad_kde = sm.nonparametric.KDEMultivariate(
          data=train_data_bad,
          var_type=self.kde_vartypes,
          bw=bw_estimation,
      )
      good_kde = sm.nonparametric.KDEMultivariate(
          data=train_data_good,
          var_type=self.kde_vartypes,
          bw=bw_estimation,
      )
  
      bad_kde.bw = np.clip(bad_kde.bw, self.min_bandwidth, None)
      good_kde.bw = np.clip(good_kde.bw, self.min_bandwidth, None)
  
      self.kde_models[budget] = {"good": good_kde, "bad": bad_kde}
  
      # update probs for the categorical parameters for later sampling
      self.logger.debug(
          "done building a new model for budget %f based on %i/%i split\nBest loss for this budget:%f\n\n\n\n\n"
          % (budget, n_good, n_bad, np.min(train_losses))
      )
  
file_path: auptimizer/src/aup/Proposer/hpbandster/optimizers/config_generators/bohb.py
imports: import logging
  from copy import deepcopy
  import traceback
  import ConfigSpace
  import ConfigSpace.hyperparameters
  import ConfigSpace.util
  import numpy
  import scipy.stats
  import scipy.optimize
  import statsmodels.api
  from hpbandster.core.base_config_generator import base_config_generator
label_desc: Use image node_id 5 for calling the BOHB obj's underlying member method code with example usage: obj.new_result(job, update_model) without return types