Further optimizations, error-checking, user-input conversions

1) The initialization now accepts "general" inputs that should apply to each gene. For example, rather than a gene input of [1,100] being interpreted to mean gene 1 hsould be 1 and gene 2 should be 100, it will apply a range of [1,100] to each gene.
2) The initialization now accepts "general" gene_input_types. For example, if the user had a set of index-dependent number values, they could just say ga.gene_input_type = "domain" and the package will propagate that across all genes in the chromosome. The user still has the option of defining the entire array or just defining a specific element if they so choose. For later commits, the general gene_input_type will have to be checked for validity; for example, a string can never be a range.
3) Fixed an issue in the ordering of the initialization function call.
4) Added comments surrounding the signfiicant changes to the initialization.
5) Added example tests to the testing file.
This commit is contained in:
RyleyGG
2020-09-25 18:02:45 -04:00
parent 922d046b72
commit 6aec9770b6
4 changed files with 68 additions and 22 deletions

View File

@ -20,10 +20,11 @@ class GA:
# Defualt EastGA implimentation structure # Defualt EastGA implimentation structure
self.gene_function_impl = random_gene self.gene_function_impl = random_gene
self.gene_input = [] self.gene_input = []
self.gene_input_type = [] #What if user gives two numbers (i.e. [1,100]) but wants to pick between the two (domain)? self.gene_input_type = []
while len(self.gene_input_type) != self.chromosome_length: while len(self.gene_input_type) != self.chromosome_length:
self.gene_input_type.append(None) self.gene_input_type.append(None)
# Set the GA Configuration # Set the GA Configuration
self.initialization_impl = random_initialization self.initialization_impl = random_initialization
#self.mutation_impl = PerGeneMutation(Mutation_rate) #self.mutation_impl = PerGeneMutation(Mutation_rate)
@ -36,27 +37,63 @@ class GA:
def initialize(self, gene_input): def initialize(self, gene_input):
self.gene_input = gene_input self.gene_input = gene_input
#assuming domain if string (strings can never be range) #It's possible user may just enter "domain", "float-range", etc. for gene_input_type rather than referring to a specific gene
for x in range(len(self.gene_input)): #In that case, create an array with the same length as the chromosome where each element is just the user's input
if isinstance(gene_input[x], list) == False: if isinstance(self.gene_input_type, str):
self.gene_input[x] = [self.gene_input[x], self.gene_input[x]] gene_input_type_temp = self.gene_input_type
self.gene_input_type = []
while len(self.gene_input_type) != self.chromosome_length:
self.gene_input_type.append(gene_input_type_temp)
if self.gene_input_type[x] == None: #If it hasn't been hard-set by the user
#There are two types of gene_input we should expect from the user - "general" or index-dependent
#For example, if a user just enters [1,100], it should be assumed that this is a range/domain that should apply to all genes in the chromosome...
#...rather than assuming that it means gene 1 should be 1 and gene 2 should be 100.
#The check for this is done by checking if any of the values in the user's gene input are lists.
#If lists are included, then values like the ones given above will be converted (i.e. [1, 100, ["up", "left"]] becomes [[1,1], [100,100], ["up", "left"]]) and apply to specific genes
#Else if no lists are included, the gene input will apply to each gene (i.e. for chromosomes with length 3, [1,100] becomes [[1,100],[1,100],[1,100]])
general_gene_input = True
for n in range(len(self.gene_input)):
if isinstance(self.gene_input[n], list):
general_gene_input = False
break
#Converting user's input into standardized format - list of lists where each sublist is the range/domain for a specific gene
if general_gene_input == False:
for x in range(len(self.gene_input)):
if isinstance(self.gene_input[x], list) == False:
self.gene_input[x] = [self.gene_input[x], self.gene_input[x]]
else:
gene_input_temp = self.gene_input
self.gene_input = []
for y in range(self.chromosome_length):
self.gene_input.append(gene_input_temp)
#Setting up the gene_input_type defaults in the standard format
#values including strings are always domain
#values including floats but no strings is a float-range
#values included neither strings or floats is a normal range
for x in range(len(self.gene_input_type)):
try:
if (self.gene_input[x]):
pass
except IndexError:
self.gene_input.append(None)
if self.gene_input_type[x] == None and self.gene_input[x] != None: #If it hasn't been hard-set by the user
for y in range(len(self.gene_input[x])): for y in range(len(self.gene_input[x])):
if isinstance(gene_input[x][y], str): if isinstance(self.gene_input[x][y], str):
self.gene_input_type[x] = "domain" self.gene_input_type[x] = "domain"
break break
elif isinstance(gene_input[x][y], float): elif isinstance(self.gene_input[x][y], float):
self.gene_input_type[x] = "float-range" self.gene_input_type[x] = "float-range"
elif y == (len(self.gene_input[x]) -1 and self.gene_input_type[x] != "float-range"): elif y == (len(self.gene_input[x]) -1 and self.gene_input_type[x] != "float-range"):
self.gene_input_type[x] = "range" self.gene_input_type[x] = "range"
# Create the first population # Create the first population
self.population = self.initialization_impl( self.population = self.initialization_impl(
self.population_size,
self.chromosome_length, self.chromosome_length,
self.population_size,
self.gene_function_impl, self.gene_function_impl,
self.gene_input, self.gene_input,
self.gene_input_type) self.gene_input_type)

View File

@ -11,7 +11,7 @@ def check_values(low,high):
def random_gene(gene_input, gene_input_type, gene_index): def random_gene(gene_input, gene_input_type, gene_index):
created_gene = None created_gene = None
#Determining if single range/domain or index-dependent
if gene_input_type[gene_index] == "range": if gene_input_type[gene_index] == "range":
created_gene = random.randint(gene_input[gene_index][0], gene_input[gene_index][1]) created_gene = random.randint(gene_input[gene_index][0], gene_input[gene_index][1])
elif gene_input_type[gene_index] == "domain": elif gene_input_type[gene_index] == "domain":

View File

@ -13,7 +13,7 @@ def random_initialization(chromosome_length,population_size,gene_function,gene_i
for i in range(population_size): for i in range(population_size):
chromosome = create_chromosome() chromosome = create_chromosome()
#Fill the Chromosome with genes #Fill the Chromosome with genes
for j in range(chromosome_length-1): for j in range(chromosome_length):
chromosome.add_gene(create_gene(gene_function(gene_input, gene_input_type, j))) chromosome.add_gene(create_gene(gene_function(gene_input, gene_input_type, j)))
population.add_chromosome(chromosome) population.add_chromosome(chromosome)
return population return population

View File

@ -10,18 +10,27 @@ import random
# Create the Genetic algorithm # Create the Genetic algorithm
ga = EasyGA.GA() ga = EasyGA.GA()
test_gene_input = [["left", "right"],[1,100],[5.0,10],[22,"up"]] test_gene_input = [["left", "right"],[1,100],[5.0,10],[22,"up"]]
#ga.gene_input_type[1] = "domain" ga.gene_input_type[1] = "float-range"
#ga.gene_input_type[1] = "float-range" ga.gene_input_type[2] = "domain"
ga.initialize(test_gene_input) ga.initialize(test_gene_input)
ga.population.print_all() ga.population.print_all()
#Example tests
#Note, the following examples assume a chromosome length of 4.
#if the test_gene_input is longer than the chromosomes, it will get truncated at the length of the chromosome
#for example, for chromosomes with length 2, [["left", "right"],[1,100],[5.0,10],[22,"up"]] becomes [["left", "right"],[1,100]]
#if the test_gene_input is shorter than the chromosomes, the remaining elements will be populated with None
#test_gene_input = [1,100]
#test_gene_input = [["left", "right"],[1,100],[5.0,10],[22,"up"]]
#test_gene_input = ["left", "right", "up", "down"]
#test_gene_input = [[1,100],[0,1],[33,35],[5,6]]
#test_gene_input = [["left", "right"], ["up", "down"], ["left", "down"], ["down", "right"]]
#test_range_one = [1,100] #ga.gene_input_type = "float-range"
#test_domain_one = ["left", "right", "up", "down"] #ga.gene_input_type[1] = "domain"
#test_range_two = [[1,100],[0,1],[33,35],[5,6]] #ga.gene_input_type[1] = "float-range"
#test_domain_two = [["left", "right"], ["up", "down"], ["left", "down"], ["down", "right"]]
#for index-specific bounds, do list of lists i.e. test_range = [[1, 100], [1, 25], [5, 25]]