Update crossover_methods.py
- Added check_weight decorator. - Implemented better random floats with weights that allow weighting all the way to either side.
This commit is contained in:
@ -11,6 +11,22 @@ def append_to_next_population(population_method):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def check_weight(individual_method):
|
||||||
|
"""Checks if the weight is between 0 and 1 before running.
|
||||||
|
Exception may occur when using ga.adapt, which will catch
|
||||||
|
the error and try again with valid weight.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def new_method(ga, parent_1, parent_2, weight):
|
||||||
|
|
||||||
|
if 0 < weight < 1:
|
||||||
|
return individual_method(ga, parent_1, parent_2, weight)
|
||||||
|
else:
|
||||||
|
raise ValueError("Weight must be between 0 and 1 when using the given crossover method.")
|
||||||
|
|
||||||
|
return new_method
|
||||||
|
|
||||||
|
|
||||||
def genes_to_chromosome(individual_method):
|
def genes_to_chromosome(individual_method):
|
||||||
"""Converts a collection of genes into a chromosome.
|
"""Converts a collection of genes into a chromosome.
|
||||||
Note: Will recreate the gene list if given gene list.
|
Note: Will recreate the gene list if given gene list.
|
||||||
@ -41,6 +57,7 @@ class Crossover_Methods:
|
|||||||
|
|
||||||
# Private method decorators, see above.
|
# Private method decorators, see above.
|
||||||
_append_to_next_population = append_to_next_population
|
_append_to_next_population = append_to_next_population
|
||||||
|
_check_weight = check_weight
|
||||||
_genes_to_chromosome = genes_to_chromosome
|
_genes_to_chromosome = genes_to_chromosome
|
||||||
_values_to_genes = values_to_genes
|
_values_to_genes = values_to_genes
|
||||||
|
|
||||||
@ -84,24 +101,21 @@ class Crossover_Methods:
|
|||||||
"""Methods for crossing parents."""
|
"""Methods for crossing parents."""
|
||||||
|
|
||||||
|
|
||||||
|
@check_weight
|
||||||
@genes_to_chromosome
|
@genes_to_chromosome
|
||||||
def single_point(ga, parent_1, parent_2, weight = 0.5):
|
def single_point(ga, parent_1, parent_2, weight = 0.5):
|
||||||
"""Cross two parents by swapping genes at one random point."""
|
"""Cross two parents by swapping genes at one random point."""
|
||||||
|
|
||||||
N = min(len(parent_1), len(parent_2))
|
|
||||||
|
|
||||||
# Equally weighted indexes
|
# Equally weighted indexes
|
||||||
if weight == 0.5:
|
if weight == 0.5:
|
||||||
swap_index = random.randrange(N)
|
swap_index = random.randrange(N)
|
||||||
|
|
||||||
# Use weighted random index.
|
# Use weighted random index.
|
||||||
else:
|
else:
|
||||||
weights = [
|
n = min(len(parent_1), len(parent_2))
|
||||||
weight*(n+1) + (1-weight)*(N-n)
|
t = 2*weight if (weight < 0.5) else 0.5 / (1-weight)
|
||||||
for n
|
x = random.random()
|
||||||
in range(N)
|
swap_index = int(n * (1-(1-x)**t)**(1/t))
|
||||||
]
|
|
||||||
swap_index = random.choices(range(N), weights)[0]
|
|
||||||
|
|
||||||
# Randomly choose which parent's genes are selected first.
|
# Randomly choose which parent's genes are selected first.
|
||||||
if random.choice([True, False]):
|
if random.choice([True, False]):
|
||||||
@ -110,12 +124,14 @@ class Crossover_Methods:
|
|||||||
return parent_2[:-swap_index] + parent_1[-swap_index:]
|
return parent_2[:-swap_index] + parent_1[-swap_index:]
|
||||||
|
|
||||||
|
|
||||||
|
@check_weight
|
||||||
@genes_to_chromosome
|
@genes_to_chromosome
|
||||||
def multi_point(ga, parent_1, parent_2, weight = 0.5):
|
def multi_point(ga, parent_1, parent_2, weight = 0.5):
|
||||||
"""Cross two parents by swapping genes at multiple points."""
|
"""Cross two parents by swapping genes at multiple points."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@check_weight
|
||||||
@genes_to_chromosome
|
@genes_to_chromosome
|
||||||
def uniform(ga, parent_1, parent_2, weight = 0.5):
|
def uniform(ga, parent_1, parent_2, weight = 0.5):
|
||||||
"""Cross two parents by swapping all genes randomly."""
|
"""Cross two parents by swapping all genes randomly."""
|
||||||
@ -127,24 +143,6 @@ class Crossover_Methods:
|
|||||||
class Arithmetic:
|
class Arithmetic:
|
||||||
"""Crossover methods for numerical genes."""
|
"""Crossover methods for numerical genes."""
|
||||||
|
|
||||||
@genes_to_chromosome
|
|
||||||
@values_to_genes
|
|
||||||
def random(ga, parent_1, parent_2, weight = 0.5):
|
|
||||||
"""Cross two parents by taking a random integer or float value between each of the genes."""
|
|
||||||
|
|
||||||
values_1 = parent_1.gene_value_iter
|
|
||||||
values_2 = parent_2.gene_value_iter
|
|
||||||
|
|
||||||
for value_1, value_2 in zip(values_1, values_2):
|
|
||||||
|
|
||||||
value = weight*values_1 + (1-weight)*random.uniform(value_1, value_2)
|
|
||||||
|
|
||||||
if type(value_1) == type(value_2) == int:
|
|
||||||
value = round(value + random.uniform(-0.5, 0.5))
|
|
||||||
|
|
||||||
yield value
|
|
||||||
|
|
||||||
|
|
||||||
@genes_to_chromosome
|
@genes_to_chromosome
|
||||||
@values_to_genes
|
@values_to_genes
|
||||||
def average(ga, parent_1, parent_2, weight = 0.5):
|
def average(ga, parent_1, parent_2, weight = 0.5):
|
||||||
@ -182,3 +180,32 @@ class Crossover_Methods:
|
|||||||
value = round(value + random.uniform(-0.5, 0.5))
|
value = round(value + random.uniform(-0.5, 0.5))
|
||||||
|
|
||||||
yield value
|
yield value
|
||||||
|
|
||||||
|
|
||||||
|
@check_weight
|
||||||
|
@genes_to_chromosome
|
||||||
|
@values_to_genes
|
||||||
|
def random(ga, parent_1, parent_2, weight = 0.5):
|
||||||
|
"""Cross two parents by taking a random integer or float value between each of the genes."""
|
||||||
|
|
||||||
|
values_1 = parent_1.gene_value_iter
|
||||||
|
values_2 = parent_2.gene_value_iter
|
||||||
|
|
||||||
|
for value_1, value_2 in zip(values_1, values_2):
|
||||||
|
|
||||||
|
# Use equally weighted values.
|
||||||
|
if weight == 0.5:
|
||||||
|
value = random.uniform(value_1, value_2)
|
||||||
|
|
||||||
|
# Use weighted random value, which gives values closer
|
||||||
|
# to value_1 if weight < 0.5 or values closer to value_2
|
||||||
|
# if weight > 0.5.
|
||||||
|
else:
|
||||||
|
t = 2*weight if (weight < 0.5) else 0.5 / (1-weight)
|
||||||
|
x = random.random()
|
||||||
|
value = value_1 + (value_2-value_1) * (1-(1-x)**t)**(1/t)
|
||||||
|
|
||||||
|
if type(value_1) == type(value_2) == int:
|
||||||
|
value = round(value + random.uniform(-0.5, 0.5))
|
||||||
|
|
||||||
|
yield value
|
||||||
|
|||||||
Reference in New Issue
Block a user