Source code for evolutionary_optimization.genotype.implemented_genotypes.float_list_genotype

from random import uniform, randint
from typing import Tuple, List, Optional

import numpy as np

from evolutionary_optimization.genotype.genotype_model.genotype_utils import single_point_crossover


[docs]class FloatListGenotype:
[docs] def __init__( self, genotype: Optional[List[float]] = None, mutation_probability: float = 0.5, ratio_of_population_for_crossover: float = 0, number_of_genes: int = 1, value_range: Tuple[int, int] = (-10000, 10000), ): """Initialise instance of AbstractGenotype. Args: genotype: genotype used for mutation, crossover and to calculate phenotype_value. mutation_probability: probability of a gene mutating. ratio_of_population_for_crossover: ratio of population used for crossover when updating population. number_of_genes: number of genes in the genotype. value_range: minimum and maximum values of a gene. Todo: * (Marta): How to deal with genotype typing. * (Marta): value range maybe shouldn't be in the constructor """ self._genotype = genotype self.mutation_probability = mutation_probability self.ratio_of_population_for_crossover = ratio_of_population_for_crossover self.number_of_genes = number_of_genes self.value_range = value_range
@property def genotype(self): """Genotype value used for evaluation of phenotype.""" return self._genotype @genotype.setter def genotype(self, value): """Genotype attribute setter.""" self._genotype = value
[docs] @classmethod def build_random_genotype( cls, number_of_genes: int = 1, value_range: Tuple[int, int] = (-10000, 10000), mutation_probability: float = 0.5, ratio_of_population_for_crossover: float = 0, ) -> "AbstractGenotype": """Build random genotype attribute based on class parameters. Args: mutation_probability: probability of a gene mutating. ratio_of_population_for_crossover: ratio of population used for crossover when updating population. Returns: AbstractGenotype object with a randomly generated genotype attribute. """ genotype = [] for i in range(number_of_genes): new_gene = uniform(value_range[0], value_range[1]) genotype.append(new_gene) return cls( genotype=genotype, mutation_probability=mutation_probability, ratio_of_population_for_crossover=ratio_of_population_for_crossover, number_of_genes=number_of_genes, value_range=value_range, )
[docs] @classmethod def from_genotype(cls, base_genotype: "FloatListGenotype", new_genotype: List[float]) -> "FloatListGenotype": """Create a new genotype using the parameters of an existing genotype.""" return cls( genotype=new_genotype, value_range=base_genotype.value_range, mutation_probability=base_genotype.mutation_probability, ratio_of_population_for_crossover=base_genotype.ratio_of_population_for_crossover, )
[docs] def mutate(self): """In place modification of the genotype by randomly changing genes based on mutation probability.""" noise = np.random.normal(0, 1, len(self.genotype)) mutation_mask = np.random.choice([True, False], p=[self.mutation_probability, 1 - self.mutation_probability], size=len(self.genotype)).astype(int) mutation_noise = noise * mutation_mask self.genotype += mutation_noise
[docs] def crossover(self, parent_2_genotype: "AbstractGenotype") -> Tuple["AbstractGenotype", "AbstractGenotype"]: """Perform crossover between two phenotypes. Combines a portion of this object's genotype with that of parent_2 to return 2 new phenotypes based on the combined genotypes. The new genotype length is the same as of the parents. Args: parent_2_genotype: a genotype of the same class whose genotype will be mixed with Returns: Two new phenotype instances based on the combined genotypes of the two parents. """ if len(self.genotype) != len(parent_2_genotype.genotype): raise NameError("The Individuals have genotypes of different lengths - crossover is impossible") if self.number_of_genes == 1: return self, parent_2_genotype else: last_slice_index = self.number_of_genes - 1 gene_slice_index = randint(1, last_slice_index) child_1_genotype = single_point_crossover(self.genotype, parent_2_genotype.genotype, gene_slice_index) child_2_genotype = single_point_crossover(parent_2_genotype.genotype, self.genotype, gene_slice_index) child_1 = self.from_genotype(parent_2_genotype, child_1_genotype) child_2 = self.from_genotype(parent_2_genotype, child_2_genotype) return child_1, child_2