docstr: Function to register finished runs. Every time a run has finished, this function should be called
  to register it with the loss.
  
  Parameters:
  -----------
  loss: float
      the loss of the parameters
  budget: float
      the budget of the parameters
  parameters: dict
      the parameters of this trial
  update_model: bool
      whether use this parameter to update BP model
  
  Returns
  -------
  None
docstr_tok: ["Function","to","register","finished","runs",".","Every","time","a","run","has","finished",",","this","function","should","be","called","to","register","it","with","the","loss",".","Parameters",":","--","--","--","--","--","-","loss",":","float","the","loss","of","the","parameters","budget",":","float","the","budget","of","the","parameters","parameters",":","dict","the","parameters","of","this","trial","update_model",":","bool","whether","use","this","parameter","to","update","BP","model","Returns","--","--","--","-","None"]
code: def new_result(self, loss, budget, parameters, update_model=True):
      """
      Function to register finished runs. Every time a run has finished, this function should be called
      to register it with the loss.
  
      Parameters:
      -----------
      loss: float
          the loss of the parameters
      budget: float
          the budget of the parameters
      parameters: dict
          the parameters of this trial
      update_model: bool
          whether use this parameter to update BP model
  
      Returns
      -------
      None
      """
      if loss is None:
          # One could skip crashed results, but we decided
          # assign a +inf loss and count them as bad configurations
          loss = np.inf
  
      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, parameters)
      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:
          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 = 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
      logger.debug(
          "done building a new model for budget %f based on %i/%i split\nBest loss for this budget:%f\n",
          budget,
          n_good,
          n_bad,
          np.min(train_losses),
      )
  
file_path: nni/nni/algorithms/hpo/bohb_advisor/config_generator.py
imports: import logging
  import ConfigSpace
  import ConfigSpace.hyperparameters
  import ConfigSpace.util
  import numpy
  import scipy.stats
  import statsmodels.api
label_desc: Use image node_id 6 for calling the CG_BOHB obj's underlying member method code with example usage: obj.new_result(loss, budget, parameters, update_model) without return types