Source code for lightweight_genetic_algorithm.crossover
import numpy as np
import copy
from .population import Individual, NumericGene
[docs]
class Crossover:
"""
This is a base class for all crossover methods. Crossover methods take the lists of genes of the parents as input.
"""
[docs]
def crossover(self, parent1_genes, parent2_genes):
"""
Perform crossover between two parents.
Args:
parent1_genes (List[Gene]): The first parent's genes.
parent2_genes (List[Gene]): The second parent's genes.
Returns:
List[Gene]: The child genes resulting from crossover.
"""
raise NotImplementedError("Subclass must implement this method")
[docs]
class CrossoverBetween(Crossover):
"""
Implements a 'between' crossover method where each child's gene is a random value
between the corresponding genes of the two parents.
Each gene in the child is generated by uniformly sampling a value between the values
of the corresponding genes in the parents.
Note:
- The genes must have a 'crossover_methods' attribute that includes 'between'.
- The genes must have 'value', 'low', and 'high' attributes.
"""
[docs]
def crossover(self, parent1_genes, parent2_genes):
"""
Perform 'between' crossover between two parents.
Args:
parent1_genes (List[NumericGene]): The genes from the first parent.
parent2_genes (List[NumericGene]): The genes from the second parent.
Returns:
List[NumericGene]: The child genes resulting from crossover.
Raises:
ValueError: If the 'between' crossover method is not compatible with the gene type.
"""
child_genes = []
for g1, g2 in zip(parent1_genes, parent2_genes):
if "between" not in g1.crossover_methods:
raise ValueError("The crossover method 'between' is not compatible with the gene type.")
low = min(g1.value, g2.value)
high = max(g1.value, g2.value)
value = np.random.uniform(low=low, high=high)
child_genes.append(NumericGene([g1.low, g1.high], value))
return child_genes
[docs]
class CrossoverMidpoint(Crossover):
"""
Implements a 'midpoint' crossover method where each child's gene is the average
(midpoint) of the corresponding genes of the two parents.
Note:
- The genes must have a 'crossover_methods' attribute that includes 'midpoint'.
- The genes must have 'value', 'low', and 'high' attributes.
"""
[docs]
def crossover(self, parent1_genes, parent2_genes):
"""
Perform 'midpoint' crossover between two parents.
Args:
parent1_genes (List[NumericGene]): The genes from the first parent.
parent2_genes (List[NumericGene]): The genes from the second parent.
Returns:
List[NumericGene]: The child genes resulting from crossover.
Raises:
ValueError: If the 'midpoint' crossover method is not compatible with the gene type.
"""
child_genes = []
for g1, g2 in zip(parent1_genes, parent2_genes):
if "midpoint" not in g1.crossover_methods:
raise ValueError("The crossover method 'midpoint' is not compatible with the gene type.")
value = (g1.value + g2.value) / 2
child_genes.append(NumericGene([g1.low, g1.high], value))
return child_genes
[docs]
class CrossoverEitherOr(Crossover):
"""
Implements an 'either or' crossover method where each child's gene is randomly chosen
to be either the corresponding gene from the first parent or from the second parent,
with equal probability.
Note:
- The genes must have a 'crossover_methods' attribute that includes 'either or'.
"""
[docs]
def crossover(self, parent1_genes, parent2_genes):
"""
Perform 'either or' crossover between two parents.
Args:
parent1_genes (List[Gene]): The genes from the first parent.
parent2_genes (List[Gene]): The genes from the second parent.
Returns:
List[Gene]: The child genes resulting from crossover. Each gene is chosen randomly from either parent.
Raises:
ValueError: If the 'either or' crossover method is not compatible with the gene type.
Notes:
- The returned genes are copies of the parent genes. This prevents unintended side effects if genes are mutable.
"""
child_genes = []
for g1, g2 in zip(parent1_genes, parent2_genes):
if "either or" not in g1.crossover_methods:
raise ValueError("The crossover method 'either or' is not compatible with the gene type.")
gene = copy.deepcopy(g1) if np.random.rand() < 0.5 else copy.deepcopy(g2)
child_genes.append(gene)
return child_genes