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:
@ -20,7 +20,8 @@ class GA:
|
||||
# Defualt EastGA implimentation structure
|
||||
self.gene_function_impl = random_gene
|
||||
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:
|
||||
self.gene_input_type.append(None)
|
||||
|
||||
@ -36,27 +37,63 @@ class GA:
|
||||
def initialize(self, gene_input):
|
||||
self.gene_input = gene_input
|
||||
|
||||
#assuming domain if string (strings can never be range)
|
||||
for x in range(len(self.gene_input)):
|
||||
if isinstance(gene_input[x], list) == False:
|
||||
self.gene_input[x] = [self.gene_input[x], self.gene_input[x]]
|
||||
#It's possible user may just enter "domain", "float-range", etc. for gene_input_type rather than referring to a specific gene
|
||||
#In that case, create an array with the same length as the chromosome where each element is just the user's input
|
||||
if isinstance(self.gene_input_type, str):
|
||||
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])):
|
||||
if isinstance(gene_input[x][y], str):
|
||||
if isinstance(self.gene_input[x][y], str):
|
||||
self.gene_input_type[x] = "domain"
|
||||
break
|
||||
elif isinstance(gene_input[x][y], float):
|
||||
elif isinstance(self.gene_input[x][y], float):
|
||||
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"
|
||||
|
||||
|
||||
|
||||
# Create the first population
|
||||
self.population = self.initialization_impl(
|
||||
self.population_size,
|
||||
self.chromosome_length,
|
||||
self.population_size,
|
||||
self.gene_function_impl,
|
||||
self.gene_input,
|
||||
self.gene_input_type)
|
||||
|
||||
@ -11,7 +11,7 @@ def check_values(low,high):
|
||||
|
||||
def random_gene(gene_input, gene_input_type, gene_index):
|
||||
created_gene = None
|
||||
#Determining if single range/domain or index-dependent
|
||||
|
||||
if gene_input_type[gene_index] == "range":
|
||||
created_gene = random.randint(gene_input[gene_index][0], gene_input[gene_index][1])
|
||||
elif gene_input_type[gene_index] == "domain":
|
||||
|
||||
@ -13,7 +13,7 @@ def random_initialization(chromosome_length,population_size,gene_function,gene_i
|
||||
for i in range(population_size):
|
||||
chromosome = create_chromosome()
|
||||
#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)))
|
||||
population.add_chromosome(chromosome)
|
||||
return population
|
||||
|
||||
@ -10,18 +10,27 @@ import random
|
||||
|
||||
# Create the Genetic algorithm
|
||||
ga = EasyGA.GA()
|
||||
|
||||
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.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]
|
||||
#test_domain_one = ["left", "right", "up", "down"]
|
||||
#test_range_two = [[1,100],[0,1],[33,35],[5,6]]
|
||||
#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]]
|
||||
#ga.gene_input_type = "float-range"
|
||||
#ga.gene_input_type[1] = "domain"
|
||||
#ga.gene_input_type[1] = "float-range"
|
||||
Reference in New Issue
Block a user