class_docstr: Population based training (PBT) as introduced by Jaderberg et al. 2017.
  
  PBT trains a generation of ``population_size`` seed trials (randomly initialized) for a user
  specified number of iterations e.g. one epoch. The top 80% then move on
  unchanged into the second generation. The bottom 20% are re-sampled from
  the top 20% and perturbed. The second generation again trains for the same
  number of iterations and the same procedure is repeated to move into the
  third generation etc.
  
  Args:
      num_generations (int): the number of generations to run for.
      population_size (int): the number of randomly intialized trials at the
          beginning and number of concurrent trials after that.
      parameter_range (dict[Union[list,tuple]): upper and lower bounds beyond
          which parameters cannot be perturbed.
      perturbation_factors (tuple[float]): the factors by which continuous
          parameters are multiplied upon perturbation; one is sampled randomly
          at a time.
class_docstr_tok: ["Population","based","training","(","PBT",")","as","introduced","by","Jaderberg","et","al",".","2017",".","PBT","trains","a","generation","of","``","population_size","``","seed","trials","(","randomly","initialized",")","for","a","user","specified","number","of","iterations","e.g",".","one","epoch",".","The","top","80","%","then","move","on","unchanged","into","the","second","generation",".","The","bottom","20","%","are","re-sampled","from","the","top","20","%","and","perturbed",".","The","second","generation","again","trains","for","the","same","number","of","iterations","and","the","same","procedure","is","repeated","to","move","into","the","third","generation","etc",".","Args",":","num_generations","(","int",")",":","the","number","of","generations","to","run","for",".","population_size","(","int",")",":","the","number","of","randomly","intialized","trials","at","the","beginning","and","number","of","concurrent","trials","after","that",".","parameter_range","(","dict","[","Union","[","list",",","tuple","]",")",":","upper","and","lower","bounds","beyond","which","parameters","can","not","be","perturbed",".","perturbation_factors","(","tuple","[","float","]",")",":","the","factors","by","which","continuous","parameters","are","multiplied","upon","perturbation",";","one","is","sampled","randomly","at","a","time","."]
docstr: Randomly perturbs candidate parameters by perturbation factors.
  
  Args:
      candidate (dict): candidate parameter configuration.
      parameters (list[sherpa.core.Parameter]): parameter ranges.
  
  Returns:
      dict: perturbed parameter configuration.
docstr_tok: ["Randomly","perturbs","candidate","parameters","by","perturbation","factors",".","Args",":","candidate","(","dict",")",":","candidate","parameter","configuration",".","parameters","(","list","[","sherpa.core.Parameter","]",")",":","parameter","ranges",".","Returns",":","dict",":","perturbed","parameter","configuration","."]
code: def _perturb(self, candidate, parameters):
      """
      Randomly perturbs candidate parameters by perturbation factors.
  
      Args:
          candidate (dict): candidate parameter configuration.
          parameters (list[sherpa.core.Parameter]): parameter ranges.
  
      Returns:
          dict: perturbed parameter configuration.
      """
      for param in parameters:
          if isinstance(param, Continuous) or isinstance(
              param, Discrete
          ):
              factor = sherpa_rng.choice(self.perturbation_factors)
              candidate[param.name] *= factor
  
              if isinstance(param, Discrete):
                  candidate[param.name] = int(candidate[param.name])
  
              candidate[param.name] = numpy.clip(
                  candidate[param.name],
                  min(
                      self.parameter_range.get(param.name)
                      or param.range
                  ),
                  max(
                      self.parameter_range.get(param.name)
                      or param.range
                  ),
              )
  
          elif isinstance(param, Ordinal):
              shift = sherpa_rng.choice([-1, 0, +1])
              values = (
                  self.parameter_range.get(param.name) or param.range
              )
              newidx = values.index(candidate[param.name]) + shift
              newidx = numpy.clip(newidx, 0, len(values) - 1)
              candidate[param.name] = values[newidx]
  
          elif isinstance(param, Choice):
              candidate[param.name] = param.sample()
  
          else:
              raise ValueError("Unrecognized Parameter Object.")
  
      return candidate
  
imports: import os
  import random
  import numpy
  import logging
  import sherpa
  import pandas
  import sklearn.gaussian_process
  from sherpa.core import Choice, Continuous, Discrete, Ordinal, AlgorithmState
  from sherpa.core import rng
  import sklearn.model_selection
  import collections
label_desc: Use image node_id 4 for calling the PopulationBasedTraining obj's underlying member method code with example usage: obj._perturb(candidate, parameters) and returns: candidate