docs: def variation(self,parents):
          """performs variation operators on parents."""
          # downselect to features that are important
          if (self.valid(parents) and
              self.ml_type != 'SVC' and self.ml_type != 'SVR'):
              # this is needed because svm has a bug that throws valueerror on
              # attribute check
              if hasattr(self.pipeline.named_steps['ml'],'coef_'):
                  # for l1 regularization, filter individuals with 0 coefficients
                  if self.weight_parents:
                      weights = self.pipeline.named_steps['ml'].coef_
                      if len(weights.shape)>1: # handle multi-coefficient models
                          weights = [np.mean(abs(c)) for c in weights.transpose()]
                      # softmax transformation of the weights
                      weights = np.exp(weights)/np.sum(np.exp(weights))
                      offspring = copy.deepcopy(
                          list(self.random_state.choice(self.valid(parents),
                                                self.population_size, p=weights)))
                  else:
                      offspring = copy.deepcopy(list(
                          x for i,x in zip(self.pipeline.named_steps['ml'].coef_,
                                           self.valid(parents)) if  (i != 0).any()))
              elif hasattr(self.pipeline.named_steps['ml'],'feature_importances_'):
                  # for tree methods, filter our individuals with 0 feature importance
                  if self.weight_parents:
                      weights = self.pipeline.named_steps['ml'].feature_importances_
                      # softmax transformation of the weights
                      weights = np.exp(weights)/np.sum(np.exp(weights))
                      offspring = copy.deepcopy(list(
                          self.random_state.choice(self.valid(parents),
                                           self.population_size, p=weights)))
                  else:
                      offspring = copy.deepcopy(list(
                          x for i,x in zip(self.pipeline.named_steps['ml'].feature_importances_,
                                           self.valid(parents)) if  i != 0))
              else:
                  offspring = copy.deepcopy(self.valid(parents))
          else:
              offspring = copy.deepcopy(self.valid(parents))
  
          if self.elitism: # keep a copy of the elite individual
              elite_index = np.argmin([x.fitness for x in parents])
              elite = copy.deepcopy(parents[elite_index])
  
          # Apply crossover and mutation on the offspring
          if self.verbosity > 2: print("variation...")
          for child1, child2 in it.zip_longest(offspring[::2], offspring[1::2],
                                               fillvalue=None):
  
              if self.random_state.rand() < self.crossover_rate and child2 != None:
              # crossover
                  self.cross(child1.stack, child2.stack, self.max_depth)
                  # update ids
                  child1.parentid = [child1.id,child2.id]
                  child1.id = uuid.uuid4()
                  child2.parentid = [child1.id,child2.id]
                  child2.id = uuid.uuid4()
                  # set default fitness
                  child1.fitness = -1
                  child2.fitness = -1
              elif child2 == None:
              # single mutation
                  self.mutate(child1.stack,self.func_set,self.term_set)
                  # update ids
                  child1.parentid = [child1.id]
                  child1.id = uuid.uuid4()
                  # set default fitness
                  child1.fitness = -1
              else:
              #double mutation
                  self.mutate(child1.stack,self.func_set,self.term_set)
                  self.mutate(child2.stack,self.func_set,self.term_set)
                  # update ids
                  child1.parentid = [child1.id]
                  child1.id = uuid.uuid4()
                  child2.parentid = [child2.id]
                  child2.id = uuid.uuid4()
                  # set default fitness
                  child1.fitness = -1
                  child2.fitness = -1
  
          while len(offspring) < self.population_size:
              #make new offspring to replace the invalid ones
              offspring.append(Ind())
              self.make_program(offspring[-1].stack,self.func_set,self.term_set,
                                self.random_state.randint(self.min_depth,
                                                          self.max_depth+1),
                                self.otype)
              offspring[-1].stack = list(reversed(offspring[-1].stack))
          
          return offspring,elite,elite_index