From 1ba86c06610f1fa221bf416ad939bc874338a0a1 Mon Sep 17 00:00:00 2001 From: SimpleArt <71458112+SimpleArt@users.noreply.github.com> Date: Thu, 26 Nov 2020 21:30:49 -0500 Subject: [PATCH] Cleaned up spacing --- src/EasyGA.py | 20 ++++--- src/attributes.py | 22 ++++---- src/database/matplotlib_graph.py | 20 +++---- src/database/sql_database.py | 56 ++++++++++++------- src/initialization/initialization_methods.py | 21 ++++--- src/mutation/mutation_methods.py | 33 ++++++----- .../parent_selection_methods.py | 40 ++++++++----- src/structure/population.py | 6 +- .../survivor_selection_methods.py | 19 +++++-- src/termination_point/termination_methods.py | 26 ++++----- 10 files changed, 152 insertions(+), 111 deletions(-) diff --git a/src/EasyGA.py b/src/EasyGA.py index eb8b961..82a84f3 100644 --- a/src/EasyGA.py +++ b/src/EasyGA.py @@ -80,14 +80,16 @@ class GA(Attributes): self.current_generation += 1 - def evolve(self): + def evolve(self, number_of_generations = 1, consider_termination = True): """Runs the ga until the termination point has been satisfied.""" + while self.active(): - self.evolve_generation() + self.evolve_generation(number_of_generations, consider_termination) def active(self): """Returns if the ga should terminate based on the termination implimented.""" + return self.termination_impl(self) @@ -96,6 +98,7 @@ class GA(Attributes): the initialization implimentation that is currently set. """ + self.population = self.initialization_impl(self) @@ -122,10 +125,10 @@ class GA(Attributes): """ return sorted( - chromosome_list, # list to be sorted - key = lambda chromosome: chromosome.fitness, # by fitness - reverse = (self.target_fitness_type == 'max') # ordered by fitness type - ) + chromosome_list, # list to be sorted + key = lambda chromosome: chromosome.fitness, # by fitness + reverse = (self.target_fitness_type == 'max') # ordered by fitness type + ) def get_chromosome_fitness(self, index): @@ -133,9 +136,10 @@ class GA(Attributes): at the specified index after conversion based on the target fitness type. """ + return self.convert_fitness( - self.population[index].fitness - ) + self.population[index].fitness + ) def convert_fitness(self, fitness_value): diff --git a/src/attributes.py b/src/attributes.py index baf20ae..fabcefd 100644 --- a/src/attributes.py +++ b/src/attributes.py @@ -35,17 +35,17 @@ class Attributes: attributes have been catigorized to explain sections in the ga process.""" target_fitness_type_dict = { - 'min' : 'min', - 'minimize' : 'min', - 'minimise' : 'min', - 'minimization' : 'min', - 'minimisation' : 'min', - 'max' : 'max', - 'maximize' : 'max', - 'maximise' : 'max', - 'maximization' : 'max', - 'maximisation' : 'max' - } + 'min' : 'min', + 'minimize' : 'min', + 'minimise' : 'min', + 'minimization' : 'min', + 'minimisation' : 'min', + 'max' : 'max', + 'maximize' : 'max', + 'maximise' : 'max', + 'maximization' : 'max', + 'maximisation' : 'max' + } def __init__(self, chromosome_length = 10, diff --git a/src/database/matplotlib_graph.py b/src/database/matplotlib_graph.py index 19ee8ce..aeb6a70 100644 --- a/src/database/matplotlib_graph.py +++ b/src/database/matplotlib_graph.py @@ -6,10 +6,10 @@ class Matplotlib_Graph: # Common graphing functions type_of_graph_dict = { - 'line' : plt.plot, - 'scatter' : plt.scatter, - 'bar' : plt.bar - } + 'line' : plt.plot, + 'scatter' : plt.scatter, + 'bar' : plt.bar + } def __init__(self, database): self.database = database @@ -31,9 +31,9 @@ class Matplotlib_Graph: # Query for Y data self.y = self.database.get_generation_total_fitness(config_id) + # If using log then the values have to be positive numbers if self.yscale == "log": - # If using log then the values have to be positive numbers - self.y = [abs(ele) for ele in self.y] + self.y = [abs(ele) for ele in self.y] self.type_of_graph(self.x, self.y) plt.xlabel('Generation') @@ -53,9 +53,9 @@ class Matplotlib_Graph: # Query for Y data self.y = self.database.get_highest_chromosome(config_id) + # If using log then the values have to be positive numbers if self.yscale == "log": - # If using log then the values have to be positive numbers - self.y = [abs(ele) for ele in self.y] + self.y = [abs(ele) for ele in self.y] self.type_of_graph(self.x, self.y) plt.xlabel('Generation') @@ -75,9 +75,9 @@ class Matplotlib_Graph: # Query for Y data self.y = self.database.get_lowest_chromosome(config_id) + # If using log then the values have to be positive numbers if self.yscale == "log": - # If using log then the values have to be positive numbers - self.y = [abs(ele) for ele in self.y] + self.y = [abs(ele) for ele in self.y] self.type_of_graph(self.x, self.y) plt.xlabel('Generation') diff --git a/src/database/sql_database.py b/src/database/sql_database.py index 371537e..a40217e 100644 --- a/src/database/sql_database.py +++ b/src/database/sql_database.py @@ -11,14 +11,17 @@ class SQL_Database: def default_config_id(method): """Decorator used to set the default config_id""" + def new_method(self, config_id = None): input_id = self.config_id if config_id is None else config_id return method(self, input_id) + return new_method def format_query_data(method): """Decorator used to format query data""" + def new_method(self, config_id): query = method(self, config_id) @@ -31,6 +34,7 @@ class SQL_Database: query = query[0] return query + return new_method @@ -48,9 +52,9 @@ class SQL_Database: def sql_type_of(self, obj): """Returns the sql type for the object""" - if type(obj) == int: + if isinstance(obj, int): return 'INT' - elif type(obj) == float: + elif isinstance(obj, float): return 'REAL' else: return 'TEXT' @@ -84,7 +88,12 @@ class SQL_Database: """ Insert one chromosome into the database""" # Structure the insert data - db_chromosome = (self.config_id, generation, chromosome.fitness, repr(chromosome)) + db_chromosome = ( + self.config_id, + generation, + chromosome.fitness, + repr(chromosome) + ) # Create sql query structure sql = ''' INSERT INTO data(config_id, generation, fitness, chromosome) @@ -101,13 +110,14 @@ class SQL_Database: # Structure the insert data db_chromosome_list = [ - ( - self.config_id, - ga.current_generation, - chromosome.fitness, - repr(chromosome) - ) - for chromosome in ga.population + ( + self.config_id, + ga.current_generation, + chromosome.fitness, + repr(chromosome) + ) + for chromosome + in ga.population ] # Create sql query structure @@ -158,9 +168,12 @@ class SQL_Database: attribute variables and adds them as columns in the database table config""" # Structure the config table - sql = "CREATE TABLE IF NOT EXISTS config (id INTEGER PRIMARY KEY," - sql += ",".join(var + ' ' + self.sql_type_of(var) for var in self.get_var_names(ga)) - sql += "); " + sql = "CREATE TABLE IF NOT EXISTS config (id INTEGER PRIMARY KEY," \ + + ",".join( + var + ' ' + self.sql_type_of(var) + for var + in self.get_var_names(ga) + ) + "); " return sql @@ -179,11 +192,11 @@ class SQL_Database: db_config_list[i] = str(db_config_list[i]) # Create sql query structure - sql = "INSERT INTO config (" - sql += ",".join(self.get_var_names(ga)) - sql += ") VALUES(" - sql += ( ",?"*len(db_config_list) )[1:] - sql += ") " + sql = "INSERT INTO config (" \ + + ",".join(self.get_var_names(ga)) \ + + ") VALUES(" \ + + ( ",?"*len(db_config_list) )[1:] \ + + ") " # For some reason it has to be in var = array(tuple()) form db_config_list = [tuple(db_config_list)] @@ -202,7 +215,6 @@ class SQL_Database: cur = self.conn.cursor() cur.execute(query) - return cur.fetchall() @@ -270,6 +282,7 @@ class SQL_Database: @property def conn(self): """Getter function for conn""" + # Return if the connection has already been set if self._conn is not None: return self._conn @@ -279,6 +292,7 @@ class SQL_Database: try: # Check if you can connect to the database self._conn = self.create_connection() + except: # if the connection doesnt exist then print error raise Exception("""You are required to run a ga before you @@ -296,6 +310,7 @@ class SQL_Database: @property def config_id(self): """Getter function for config_id""" + # Return if the config_id has already been set if self._config_id is not None: return self._config_id @@ -305,10 +320,11 @@ class SQL_Database: try: # Check if you can connect to the database self._config_id = self.get_most_recent_config_id() + except: # if the config_id doesnt exist then print error raise Exception("""You are required to run a ga before you - can connect to the database. Run ga.evolve() or ga.active()""") + can connect to the database. Run ga.evolve() or ga.active()""") @config_id.setter diff --git a/src/initialization/initialization_methods.py b/src/initialization/initialization_methods.py index edb7a7f..a006111 100644 --- a/src/initialization/initialization_methods.py +++ b/src/initialization/initialization_methods.py @@ -2,7 +2,11 @@ def chromosomes_to_population(initialize): """Makes a population from chromosomes.""" return lambda ga:\ ga.make_population( - [initialize(ga) for _ in range(ga.population_size)] + [ + initialize(ga) + for _ + in range(ga.population_size) + ] ) def genes_to_chromosome(initialize): @@ -15,19 +19,20 @@ def genes_to_chromosome(initialize): def values_to_genes(initialize): """Converts a collection of values to genes.""" return lambda ga:\ - (ga.make_gene(value) for value in initialize(ga)) + ( + ga.make_gene(value) + for value + in initialize(ga) + ) class Initialization_Methods: """Initialization examples that are used as defaults and examples""" # Private method decorators, see above. - def _chromosomes_to_population(initialize): - return chromosomes_to_population(initialize) - def _genes_to_chromosome(initialize): - return genes_to_chromosome(initialize) - def _value_to_gene(initialize): - return value_to_gene(initialize) + _chromosomes_to_population = chromosomes_to_population + _genes_to_chromosome = genes_to_chromosome + _value_to_gene = value_to_gene @chromosomes_to_population diff --git a/src/mutation/mutation_methods.py b/src/mutation/mutation_methods.py index 29c9e22..43dc904 100644 --- a/src/mutation/mutation_methods.py +++ b/src/mutation/mutation_methods.py @@ -4,7 +4,7 @@ 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_population_method(ga): + def new_method(ga): if not isinstance(ga.chromosome_mutation_rate, float): raise TypeError("Chromosome mutation rate must be a float.") @@ -15,13 +15,13 @@ def check_chromosome_mutation_rate(population_method): else: raise ValueError("Chromosome mutation rate must be between 0 and 1.") - return new_population_method + 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_individual_method(ga, index): + def new_method(ga, index): if not isinstance(ga.gene_mutation_rate, float): raise TypeError("Gene mutation rate must be a float.") @@ -32,19 +32,19 @@ def check_gene_mutation_rate(individual_method): else: raise ValueError("Gene mutation rate must be between 0 and 1.") - return new_individual_method + return new_method def loop_selections(population_method): """Runs the population method until enough chromosomes are mutated.""" - def new_population_method(ga): + def new_method(ga): # Loop the population method until enough chromosomes are mutated. for _ in range(ceil(len(ga.population)*ga.chromosome_mutation_rate)): population_method(ga) - return new_population_method + return new_method def loop_mutations(individual_method): @@ -53,26 +53,22 @@ def loop_mutations(individual_method): """ # Change input from index to chromosome. - def new_individual_method(ga, index): + def new_method(ga, index): # Loop the individual method until enough genes are mutated. for _ in range(ceil(len(ga.population[index])*ga.gene_mutation_rate)): individual_method(ga, ga.population[index]) - return new_individual_method + return new_method class Mutation_Methods: # Private method decorators, see above. - def _check_chromosome_mutation_rate(population_method): - return check_chromosome_mutation_rate(population_method) - def _check_gene_mutation_rate(individual_method): - return check_gene_mutation_rate(individual_method) - def _loop_selections(population_method): - return loop_selections(population_method) - def _loop_mutations(individual_method): - return loop_mutations(individual_method) + _check_chromosome_mutation_rate = check_chromosome_mutation_rate + _check_gene_mutation_rate = check_gene_mutation_rate + _loop_selections = loop_selections + _loop_mutations = loop_mutations class Population: @@ -92,7 +88,10 @@ class Mutation_Methods: def random_avoid_best(ga): """Selects random chromosomes while avoiding the best chromosomes. (Elitism)""" - index = random.randrange(int(len(ga.population)*ga.gene_mutation_rate/2), len(ga.population)) + index = random.randrange( + int(len(ga.population)*ga.gene_mutation_rate/2), + len(ga.population) + ) ga.mutation_individual_impl(ga, index) diff --git a/src/parent_selection/parent_selection_methods.py b/src/parent_selection/parent_selection_methods.py index 1405b02..9ca27b0 100644 --- a/src/parent_selection/parent_selection_methods.py +++ b/src/parent_selection/parent_selection_methods.py @@ -5,12 +5,14 @@ def check_selection_probability(selection_method): is not between 0 and 1. Otherwise runs the selection method. """ - def helper(ga): + + def new_method(ga): if 0 < ga.selection_probability < 1: selection_method(ga) else: raise Exception("Selection probability must be between 0 and 1 to select parents.") - return helper + + return new_method def check_positive_fitness(selection_method): @@ -18,33 +20,34 @@ def check_positive_fitness(selection_method): chromosome with negative fitness. Otherwise runs the selection method. """ - def helper(ga): + + def new_method(ga): if ga.get_chromosome_fitness(0) > 0 and ga.get_chromosome_fitness(-1) >= 0: selection_method(ga) else: raise Exception("Converted fitness values must be all positive. Consider using rank selection instead.") - return helper + + return new_method def ensure_sorted(selection_method): """Sorts the population by fitness and then runs the selection method. """ - def helper(ga): + + def new_method(ga): ga.population.sort_by_best_fitness(ga) selection_method(ga) - return helper + + return new_method class Parent_Selection: # Private method decorators, see above. - def _check_selection_probability(selection_method): - return check_selection_probability(selection_method) - def _check_positive_fitness(selection_method): - return check_positive_fitness(selection_method) - def _ensure_sorted(selection_method): - return ensure_sorted(selection_method) + _check_selection_probability = check_selection_probability + _check_positive_fitness = check_positive_fitness + _ensure_sorted = ensure_sorted class Rank: @@ -62,13 +65,16 @@ class Parent_Selection: # Use no less than 5 chromosomes per tournament. tournament_size = int(len(ga.population)*ga.tournament_size_ratio) if tournament_size < 5: - tournament_size = 5 + tournament_size = min(5, len(ga.population)) # Repeat tournaments until the mating pool is large enough. while True: # Generate a random tournament group and sort by fitness. - tournament_group = sorted([random.randrange(len(ga.population)) for _ in range(tournament_size)]) + tournament_group = sorted(random.sample( + range(len(ga.population)), + k = tournament_size + )) # For each chromosome, add it to the mating pool based on its rank in the tournament. for index in range(tournament_size): @@ -101,7 +107,11 @@ class Parent_Selection: """ # The sum of all the fitnessess in a population - fitness_sum = sum(ga.get_chromosome_fitness(index) for index in range(len(ga.population))) + fitness_sum = sum( + ga.get_chromosome_fitness(index) + for index + in range(len(ga.population)) + ) # A list of ranges that represent the probability of a chromosome getting chosen probability = [ga.selection_probability] diff --git a/src/structure/population.py b/src/structure/population.py index 82759cb..7948ddf 100644 --- a/src/structure/population.py +++ b/src/structure/population.py @@ -150,7 +150,7 @@ class Population: to get a backend representation of the population. """ return ''.join( - f'Chromosome - {index} {chromosome} ' + - f'/ Fitness = {chromosome.fitness}\n' - for index, chromosome in enumerate(self) + f'Chromosome - {index} {chromosome} / Fitness = {chromosome.fitness}\n' + for index, chromosome + in enumerate(self) ) diff --git a/src/survivor_selection/survivor_selection_methods.py b/src/survivor_selection/survivor_selection_methods.py index 0b8d057..d4c9a65 100644 --- a/src/survivor_selection/survivor_selection_methods.py +++ b/src/survivor_selection/survivor_selection_methods.py @@ -2,15 +2,16 @@ import random def append_to_next_population(survivor_method): """Appends the selected chromosomes to the next population.""" - return lambda ga: ga.population.append_children(survivor_method(ga)) + + return lambda ga:\ + ga.population.append_children(survivor_method(ga)) class Survivor_Selection: """Survivor selection determines which individuals should be brought to the next generation""" # Private method decorator, see above. - def _append_to_next_population(survivor_method): - return append_to_next_population(survivor_method) + _append_to_next_population = append_to_next_population @append_to_next_population @@ -33,8 +34,11 @@ class Survivor_Selection: def fill_in_parents_then_random(ga): """Fills in the next population with all parents followed by random chromosomes from the last population""" + # Remove dupes from the mating pool + mating_pool = set(ga.population.mating_pool) + needed_amount = len(ga.population) - len(ga.population.next_population) - parent_amount = min(len(ga.population.mating_pool), needed_amount) + parent_amount = min(needed_amount, len(mating_pool)) random_amount = needed_amount - parent_amount # Only parents are used. @@ -43,5 +47,8 @@ class Survivor_Selection: # Parents need to be removed from the random sample to avoid dupes. else: - return ga.population.mating_pool +\ - random.sample(set(ga.population)-set(ga.population.mating_pool), random_amount) + return mating_pool \ + + random.sample( + set(ga.population) - mating_pool, + random_amount + ) diff --git a/src/termination_point/termination_methods.py b/src/termination_point/termination_methods.py index 4c2a999..3ec2de0 100644 --- a/src/termination_point/termination_methods.py +++ b/src/termination_point/termination_methods.py @@ -1,7 +1,7 @@ def add_by_fitness_goal(termination_impl): """Adds termination by fitness goal to the method.""" - def helper(ga): + def new_method(ga): # If fitness goal is set, check it. if ga.fitness_goal is not None: @@ -16,13 +16,14 @@ def add_by_fitness_goal(termination_impl): # Check other termination methods return termination_impl(ga) - return helper + + return new_method def add_by_generation_goal(termination_impl): """Adds termination by generation goal to the method.""" - def helper(ga): + def new_method(ga): # If generation goal is set, check it. if ga.generation_goal is not None and ga.current_generation >= ga.generation_goal: @@ -30,18 +31,19 @@ def add_by_generation_goal(termination_impl): # Check other termination methods return termination_impl(ga) - return helper + + return new_method def add_by_tolerance_goal(termination_impl): """Adds termination by tolerance goal to the method.""" - def helper(ga): + def new_method(ga): # If tolerance is set, check it. if ga.tolerance_goal is not None: best_fitness = ga.population[0].fitness - threshhold_fitness = ga.population[int(ga.percent_converged*len(ga.population))].fitness + threshhold_fitness = ga.population[round(ga.percent_converged*len(ga.population))].fitness tol = ga.tolerance_goal * (1 + abs(best_fitness)) # Terminate if the specified amount of the population has converged to the specified tolerance @@ -50,19 +52,17 @@ def add_by_tolerance_goal(termination_impl): # Check other termination methods return termination_impl(ga) - return helper + + return new_method class Termination_Methods: """Example functions that can be used to terminate the the algorithms loop""" # Private method decorators, see above. - def _add_by_fitness_goal(termination_impl): - return add_by_fitness_goal(termination_impl) - def _add_by_generation_goal(termination_impl): - return add_by_generation_goal(termination_impl) - def _add_by_tolerance_goal(termination_impl): - return add_by_tolerance_goal(termination_impl) + _add_by_fitness_goal = add_by_fitness_goal + _add_by_generation_goal = add_by_generation_goal + _add_by_tolerance_goal = add_by_tolerance_goal @add_by_fitness_goal