168 lines
5.4 KiB
Python
168 lines
5.4 KiB
Python
import random
|
|
from math import ceil
|
|
|
|
def _check_chromosome_mutation_rate(population_method):
|
|
"""Checks if the chromosome mutation rate is a float between 0 and 1 before running."""
|
|
|
|
def new_method(ga):
|
|
|
|
if not isinstance(ga.chromosome_mutation_rate, float):
|
|
raise TypeError("Chromosome mutation rate must be a float.")
|
|
|
|
elif 0 < ga.chromosome_mutation_rate < 1:
|
|
population_method(ga)
|
|
|
|
else:
|
|
raise ValueError("Chromosome mutation rate must be between 0 and 1.")
|
|
|
|
return new_method
|
|
|
|
|
|
def _check_gene_mutation_rate(individual_method):
|
|
"""Checks if the gene mutation rate is a float between 0 and 1 before running."""
|
|
|
|
def new_method(ga, index):
|
|
|
|
if not isinstance(ga.gene_mutation_rate, float):
|
|
raise TypeError("Gene mutation rate must be a float.")
|
|
|
|
elif 0 < ga.gene_mutation_rate < 1:
|
|
individual_method(ga, index)
|
|
|
|
else:
|
|
raise ValueError("Gene mutation rate must be between 0 and 1.")
|
|
|
|
return new_method
|
|
|
|
|
|
def _reset_fitness(individual_method):
|
|
"""Resets the fitness value of the chromosome."""
|
|
|
|
def new_method(ga, chromosome):
|
|
chromosome.fitness = None
|
|
individual_method(ga, chromosome)
|
|
|
|
return new_method
|
|
|
|
|
|
def _loop_random_selections(population_method):
|
|
"""Runs the population method until enough chromosomes are mutated.
|
|
Provides the indexes of selected chromosomes to mutate using
|
|
random.sample to get all indexes fast.
|
|
"""
|
|
|
|
def new_method(ga):
|
|
|
|
sample_space = range(len(ga.population))
|
|
sample_size = ceil(len(ga.population)*ga.chromosome_mutation_rate)
|
|
|
|
# Loop the population method until enough chromosomes are mutated.
|
|
for index in random.sample(sample_space, sample_size):
|
|
population_method(ga, index)
|
|
|
|
return new_method
|
|
|
|
|
|
def _loop_random_mutations(individual_method):
|
|
"""Runs the individual method until enough
|
|
genes are mutated on the indexed chromosome.
|
|
"""
|
|
|
|
# Change input to include the gene index being mutated.
|
|
def new_method(ga, chromosome):
|
|
|
|
sample_space = range(len(chromosome))
|
|
sample_size = ceil(len(chromosome)*ga.gene_mutation_rate)
|
|
|
|
# Loop the individual method until enough genes are mutated.
|
|
for index in random.sample(sample_space, sample_size):
|
|
individual_method(ga, chromosome, index)
|
|
|
|
return new_method
|
|
|
|
|
|
class Mutation_Methods:
|
|
|
|
_check_chromosome_mutation_rate = _check_chromosome_mutation_rate
|
|
_check_gene_mutation_rate = _check_gene_mutation_rate
|
|
_reset_fitness = _reset_fitness
|
|
_loop_random_selections = _loop_random_selections
|
|
_loop_random_mutations = _loop_random_mutations
|
|
|
|
|
|
class Population:
|
|
"""Methods for selecting chromosomes to mutate"""
|
|
|
|
@_check_chromosome_mutation_rate
|
|
@_loop_random_selections
|
|
def random_selection(ga, index):
|
|
"""Selects random chromosomes."""
|
|
|
|
ga.mutation_individual_impl(ga, ga.population[index])
|
|
|
|
|
|
@_check_chromosome_mutation_rate
|
|
def random_avoid_best(ga):
|
|
"""Selects random chromosomes while avoiding the best chromosomes. (Elitism)"""
|
|
|
|
sample_space = range(ceil(ga.percent_converged*len(ga.population)*3/16), len(ga.population))
|
|
sample_size = ceil(ga.chromosome_mutation_rate*len(ga.population))
|
|
|
|
for index in random.sample(sample_space, sample_size):
|
|
ga.mutation_individual_impl(ga, ga.population[index])
|
|
|
|
|
|
class Individual:
|
|
"""Methods for mutating a single chromosome."""
|
|
|
|
@_check_gene_mutation_rate
|
|
@_reset_fitness
|
|
@_loop_random_mutations
|
|
def individual_genes(ga, chromosome, index):
|
|
"""Mutates a random gene in the chromosome."""
|
|
|
|
# Using the chromosome_impl
|
|
if ga.chromosome_impl is not None:
|
|
chromosome[index] = ga.make_gene(ga.chromosome_impl()[index])
|
|
|
|
# Using the gene_impl
|
|
elif ga.gene_impl is not None:
|
|
chromosome[index] = ga.make_gene(ga.gene_impl())
|
|
|
|
# Exit because no gene creation method specified
|
|
else:
|
|
raise Exception("Did not specify any initialization constraints.")
|
|
|
|
|
|
class Arithmetic:
|
|
"""Methods for mutating a chromosome
|
|
by numerically modifying the genes."""
|
|
|
|
@_check_gene_mutation_rate
|
|
@_reset_fitness
|
|
@_loop_random_mutations
|
|
def reflect_genes(ga, chromosome, index):
|
|
"""Reflects genes against the best chromosome."""
|
|
|
|
difference = ga.population[0][index].value - chromosome[index].value
|
|
value = ga.population[0][index].value + 2*difference
|
|
chromosome[index] = ga.make_gene(value)
|
|
|
|
|
|
class Permutation:
|
|
"""Methods for mutating a chromosome
|
|
by changing the order of the genes."""
|
|
|
|
@_check_gene_mutation_rate
|
|
@_reset_fitness
|
|
@_loop_random_mutations
|
|
def swap_genes(ga, chromosome, index):
|
|
"""Swaps two random genes in the chromosome."""
|
|
|
|
# Indexes of genes to swap
|
|
index_one = index
|
|
index_two = random.randrange(len(chromosome))
|
|
|
|
# Swap genes
|
|
chromosome[index_one], chromosome[index_two] = chromosome[index_two], chromosome[index_one]
|