Source code for pygenalgo.operators.selection.roulette_wheel_selector

""" Roulette wheel selection module. """
from math import fsum, isclose

# Custom code imports.
from pygenalgo.genome.chromosome import Chromosome
from pygenalgo.operators.genetic_operator import increase_counter
from pygenalgo.operators.selection.select_operator import (SelectionOperator,
                                                           ensure_positive_fitness)


[docs] class RouletteWheelSelector(SelectionOperator): """ Description: Roulette wheel selection employs a probabilistic mechanism where individuals are selected based on their fitness relative to the entire population. Each individual receives a slice of a wheel proportional to its fitness, similar to a casino roulette, where a random number determines the selected individual. This method encourages selection of fitter individuals while allowing lower fitness individuals a chance to contribute. While effective, it can lead to premature convergence if the population's fitness is skewed, often referred to as the "selection pressure." """ def __init__(self, select_probability: float = 1.0) -> None: """ Construct a 'RouletteWheelSelector' object with a given probability value. :param select_probability: (float) in [0, 1]. """ # Call the super constructor with the provided probability value. super().__init__(selection_probability=select_probability) # _end_def_
[docs] @increase_counter def select(self, population: list[Chromosome]) -> list[Chromosome]: """ Select the individuals, from the input population, that will be passed on to the next genetic operations of crossover and mutation to form the new population of solutions. :param population: a list of chromosomes to select the parents from. :return: the selected parents population (as list of chromosomes). """ # Get the population size. pop_size: int = len(population) # Extract the (positive) fitness values from the chromosomes. all_fitness: list[float] = ensure_positive_fitness(population) # Calculate sum of all fitness. sum_fitness: float = fsum(all_fitness) # If total fitness is zero (or effectively zero), # fall back to uniform random selection so every # individual has equal chance. if isclose(sum_fitness, 0.0, abs_tol=1e-12): # Return a new random list of parents. return self._safety_option(population) # _end_if_ # Calculate the "selection probability" # for each member in the population. selection_probs: list[float] = [ f / sum_fitness for f in all_fitness ] # Select the new individuals (indexes). index = self.rng.choice(pop_size, size=pop_size, p=selection_probs, replace=True, shuffle=False) # Return the new parents (individuals). return [population[i] for i in index]
# _end_def_ # _end_class_