From cbc2fafb072477092705a0b10a4ea4a2f22e197a Mon Sep 17 00:00:00 2001 From: SimpleArt <71458112+SimpleArt@users.noreply.github.com> Date: Wed, 30 Dec 2020 14:35:08 -0500 Subject: [PATCH] Delete files for renaming --- src/crossover/README.md | 1 - src/crossover/__init__.py | 2 - src/crossover/crossover_methods.py | 258 ----------------- src/crossover/test_crossover_methods.py | 0 src/fitness_function/README.md | 1 - src/fitness_function/__init__.py | 2 - src/fitness_function/fitness_examples.py | 36 --- src/fitness_function/test_methods.py | 1 - src/mutation/README.md | 1 - src/mutation/__init__.py | 2 - src/mutation/mutation_methods.py | 217 -------------- src/mutation/test_mutation_methods.py | 0 src/parent_selection/README.md | 1 - src/parent_selection/__init__.py | 2 - .../parent_selection_methods.py | 266 ------------------ .../test_parent_selection_methods.py | 0 src/survivor_selection/README.md | 1 - src/survivor_selection/__init__.py | 2 - .../survivor_selection_methods.py | 60 ---- .../test_survivor_methods.py | 0 src/termination_point/README.md | 1 - src/termination_point/__init__.py | 2 - src/termination_point/termination_methods.py | 99 ------- .../test_termination_methods.py | 0 24 files changed, 955 deletions(-) delete mode 100644 src/crossover/README.md delete mode 100644 src/crossover/__init__.py delete mode 100644 src/crossover/crossover_methods.py delete mode 100644 src/crossover/test_crossover_methods.py delete mode 100644 src/fitness_function/README.md delete mode 100644 src/fitness_function/__init__.py delete mode 100644 src/fitness_function/fitness_examples.py delete mode 100644 src/fitness_function/test_methods.py delete mode 100644 src/mutation/README.md delete mode 100644 src/mutation/__init__.py delete mode 100644 src/mutation/mutation_methods.py delete mode 100644 src/mutation/test_mutation_methods.py delete mode 100644 src/parent_selection/README.md delete mode 100644 src/parent_selection/__init__.py delete mode 100644 src/parent_selection/parent_selection_methods.py delete mode 100644 src/parent_selection/test_parent_selection_methods.py delete mode 100644 src/survivor_selection/README.md delete mode 100644 src/survivor_selection/__init__.py delete mode 100644 src/survivor_selection/survivor_selection_methods.py delete mode 100644 src/survivor_selection/test_survivor_methods.py delete mode 100644 src/termination_point/README.md delete mode 100644 src/termination_point/__init__.py delete mode 100644 src/termination_point/termination_methods.py delete mode 100644 src/termination_point/test_termination_methods.py diff --git a/src/crossover/README.md b/src/crossover/README.md deleted file mode 100644 index c38d93c..0000000 --- a/src/crossover/README.md +++ /dev/null @@ -1 +0,0 @@ -# Mutation functions diff --git a/src/crossover/__init__.py b/src/crossover/__init__.py deleted file mode 100644 index 417b9f4..0000000 --- a/src/crossover/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# FROM (. means local) file_name IMPORT function_name -from .crossover_methods import Crossover_Methods diff --git a/src/crossover/crossover_methods.py b/src/crossover/crossover_methods.py deleted file mode 100644 index 2943785..0000000 --- a/src/crossover/crossover_methods.py +++ /dev/null @@ -1,258 +0,0 @@ -from EasyGA import function_info -import random - -# Round to an integer near x with higher probability -# the closer it is to that integer. -randround = lambda x: int(x + random.random()) - - -@function_info -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 = individual_method.__kwdefaults__.get('weight', None)): - - if weight is None: - individual_method(ga, parent_1, parent_2) - elif 0 < weight < 1: - individual_method(ga, parent_1, parent_2, weight = weight) - else: - raise ValueError(f"Weight must be between 0 and 1 when using {individual_method.__name__}.") - - return new_method - - -@function_info -def _gene_by_gene(individual_method): - """Perform crossover by making a single new chromosome by combining each gene by gene.""" - - def new_method(ga, parent_1, parent_2, *, weight = individual_method.__kwdefaults__.get('weight', 'None')): - - ga.population.add_child( - individual_method(ga, value_1, value_2) - if weight == 'None' else - individual_method(ga, value_1, value_2, weight = weight) - for value_1, value_2 - in zip(parent_1.gene_value_iter, parent_2.gene_value_iter) - ) - - return new_method - - -class Crossover_Methods: - - # Allowing access to decorators when importing class - _check_weight = _check_weight - _gene_by_gene = _gene_by_gene - - - class Population: - """Methods for selecting chromosomes to crossover.""" - - - def sequential_selection(ga, mating_pool): - """Select sequential pairs from the mating pool. - Every parent is paired with the previous parent. - The first parent is paired with the last parent. - """ - - for index in range(len(mating_pool)): # for each parent in the mating pool - ga.crossover_individual_impl( # apply crossover to - mating_pool[index], # the parent and - mating_pool[index-1], # the previous parent - ) - - - def random_selection(ga, mating_pool): - """Select random pairs from the mating pool. - Every parent is paired with a random parent. - """ - - for parent in mating_pool: # for each parent in the mating pool - ga.crossover_individual_impl( # apply crossover to - parent, # the parent and - random.choice(mating_pool), # a random parent - ) - - - class Individual: - """Methods for crossing parents.""" - - - @_check_weight - def single_point(ga, parent_1, parent_2, *, weight = 0.5): - """Cross two parents by swapping genes at one random point.""" - - minimum_parent_length = min(len(parent_1), len(parent_2)) - - # Weighted random integer from 0 to minimum parent length - 1 - swap_index = int(ga.weighted_random(weight) * minimum_parent_length) - - ga.population.add_child(parent_1[:swap_index] + parent_2[swap_index:]) - ga.population.add_child(parent_2[:swap_index] + parent_1[swap_index:]) - - - @_check_weight - def multi_point(ga, parent_1, parent_2, *, weight = 0.5): - """Cross two parents by swapping genes at multiple points.""" - pass - - - @_check_weight - @_gene_by_gene - def uniform(ga, value_1, value_2, *, weight = 0.5): - """Cross two parents by swapping all genes randomly.""" - return random.choices(gene_pair, cum_weights = [weight, 1])[0] - - - class Arithmetic: - """Crossover methods for numerical genes.""" - - @_gene_by_gene - def average(ga, value_1, value_2, *, weight = 0.5): - """Cross two parents by taking the average of the genes.""" - - average_value = weight*value_1 + (1-weight)*value_2 - - if type(value_1) == type(value_2) == int: - average_value = randround(value) - - return average_value - - - @_gene_by_gene - def extrapolate(ga, value_1, value_2, *, weight = 0.5): - """Cross two parents by extrapolating towards the first parent. - May result in gene values outside the expected domain. - """ - - extrapolated_value = weight*value_1 + (1-weight)*value_2 - - if type(value_1) == type(value_2) == int: - extrapolated_value = randround(value) - - return extrapolated_value - - - @_check_weight - @_gene_by_gene - def random(ga, value_1, value_2, *, weight = 0.5): - """Cross two parents by taking a random integer or float value between each of the genes.""" - - value = value_1 + ga.weighted_random(weight) * (value_2-value_1) - - if type(value_1) == type(value_2) == int: - value = randround(value) - - return value - - - class Permutation: - """Crossover methods for permutation based chromosomes.""" - - @_check_weight - def ox1(ga, parent_1, parent_2, *, weight = 0.5): - """Cross two parents by slicing out a random part of one parent - and then filling in the rest of the genes from the second parent.""" - - # Too small to cross - if len(parent_1) < 2: - return parent_1.gene_list - - # Unequal parent lengths - if len(parent_1) != len(parent_2): - raise ValueError("Parents do not have the same lengths.") - - # Swap with weighted probability so that most of the genes - # are taken directly from parent 1. - if random.choices([0, 1], cum_weights = [weight, 1]) == 1: - parent_1, parent_2 = parent_2, parent_1 - - # Extract genes from parent 1 between two random indexes - index_2 = random.randrange(1, len(parent_1)) - index_1 = random.randrange(index_2) - - # Create copies of the gene lists - gene_list_1 = [None]*index_1 + parent_1[index_1:index_2] + [None]*(len(parent_1)-index_2) - gene_list_2 = list(parent_2) - - input_index = 0 - - # For each gene from the second parent - for _ in range(len(gene_list_2)): - - # Remove it if it is already used - if gene_list_2[-1] in gene_list_1: - gene_list_2.pop(-1) - - # Add it if it has not been used - else: - if input_index == index_1: - input_index = index_2 - gene_list_1[input_index] = gene_list_2.pop(-1) - input_index += 1 - - ga.population.add_child(gene_list_1) - - - @_check_weight - def partially_mapped(ga, parent_1, parent_2, *, weight = 0.5): - """Cross two parents by slicing out a random part of one parent - and then filling in the rest of the genes from the second parent, - preserving the ordering of genes wherever possible. - - NOTE: Needs to be fixed, since genes are not hashable...""" - - # Too small to cross - if len(parent_1) < 2: - return parent_1.gene_list - - # Unequal parent lengths - if len(parent_1) != len(parent_2): - raise ValueError("Parents do not have the same lengths.") - - # Swap with weighted probability so that most of the genes - # are taken directly from parent 1. - if random.choices([0, 1], cum_weights = [weight, 1]) == 1: - parent_1, parent_2 = parent_2, parent_1 - - # Extract genes from parent 1 between two random indexes - index_2 = random.randrange(1, len(parent_1)) - index_1 = random.randrange(index_2) - - # Create copies of the gene lists - gene_list_1 = [None]*index_1 + parent_1[index_1:index_2] + [None]*(len(parent_1)-index_2) - gene_list_2 = list(parent_2) - - # Create hash for gene list 2 - hash = {gene:index for index, gene in enumerate(gene_list_2)} - - # For each gene in the copied segment from parent 2 - for i in range(index_1, index_2): - - # If it is not already copied, - # find where it got displaced to - j = i - while gene_list_1[(j := hash[gene_list_1[j]])] is not None: - pass - gene_list_1[j] = gene_list_2[i] - - # Fill in whatever is leftover (copied from ox1). - # For each gene from the second parent - for _ in range(len(gene_list_2)): - - # Remove it if it is already used - if gene_list_2[-1] in gene_list_1: - gene_list_2.pop(-1) - - # Add it if it has not been used - else: - if input_index == index_1: - input_index = index_2 - gene_list_1[input_index] = gene_list_2.pop(-1) - input_index += 1 - - ga.population.add_child(gene_list_1) diff --git a/src/crossover/test_crossover_methods.py b/src/crossover/test_crossover_methods.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/fitness_function/README.md b/src/fitness_function/README.md deleted file mode 100644 index 9bf8dfd..0000000 --- a/src/fitness_function/README.md +++ /dev/null @@ -1 +0,0 @@ -# Fitness function diff --git a/src/fitness_function/__init__.py b/src/fitness_function/__init__.py deleted file mode 100644 index c1736e7..0000000 --- a/src/fitness_function/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# FROM (. means local) file_name IMPORT class name -from .fitness_examples import Fitness_Examples diff --git a/src/fitness_function/fitness_examples.py b/src/fitness_function/fitness_examples.py deleted file mode 100644 index 33e4730..0000000 --- a/src/fitness_function/fitness_examples.py +++ /dev/null @@ -1,36 +0,0 @@ -class Fitness_Examples: - """Fitness function examples used""" - - def is_it_5(chromosome): - """A very simple case test function - If the chromosomes gene value is a 5 add one - to the chromosomes overall fitness value.""" - # Overall fitness value - fitness = 0 - # For each gene in the chromosome - for gene in chromosome: - # Check if its value = 5 - if(gene.value == 5): - # If its value is 5 then add one to - # the overal fitness of the chromosome. - fitness += 1 - - return fitness - - - def near_5(chromosome): - """Test's the GA's ability to handle floats. - Computes how close each gene is to 5. - """ - return sum([1-pow(1-gene.value/5, 2) for gene in chromosome]) - - - def index_dependent_values(chromosome): - """Test of the GA's ability to improve fitness when the value is index-dependent. - If a gene is equal to its index in the chromosome + 1, fitness is incremented. - """ - fitness = 0 - for i in range(len(chromosome)): - if (chromosome[i].value == i+1): - fitness += 1 - - return fitness diff --git a/src/fitness_function/test_methods.py b/src/fitness_function/test_methods.py deleted file mode 100644 index 8b13789..0000000 --- a/src/fitness_function/test_methods.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/mutation/README.md b/src/mutation/README.md deleted file mode 100644 index c38d93c..0000000 --- a/src/mutation/README.md +++ /dev/null @@ -1 +0,0 @@ -# Mutation functions diff --git a/src/mutation/__init__.py b/src/mutation/__init__.py deleted file mode 100644 index c0fabea..0000000 --- a/src/mutation/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# FROM (. means local) file_name IMPORT function_name -from .mutation_methods import Mutation_Methods diff --git a/src/mutation/mutation_methods.py b/src/mutation/mutation_methods.py deleted file mode 100644 index b8ab89d..0000000 --- a/src/mutation/mutation_methods.py +++ /dev/null @@ -1,217 +0,0 @@ -from EasyGA import function_info -import random -from math import ceil - - -@function_info -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 - - -@function_info -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 - - -@function_info -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 - - -@function_info -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_mutations = _loop_random_mutations - - - class Population: - """Methods for selecting chromosomes to mutate""" - - @_check_chromosome_mutation_rate - def random_selection(ga): - """Selects random chromosomes.""" - - sample_space = range(len(ga.population)) - sample_size = ceil(len(ga.population)*ga.chromosome_mutation_rate) - - # Loop the individual method until enough genes are mutated. - for index in random.sample(sample_space, sample_size): - ga.mutation_individual_impl(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.population[index]) - - - @_check_chromosome_mutation_rate - def best_replace_worst(ga): - """Selects the best chromosomes, copies them, and replaces the worst chromosomes.""" - - mutation_amount = ceil(ga.chromosome_mutation_rate*len(ga.population)) - - for i in range(mutation_amount): - ga.population[-i-1] = ga.make_chromosome(ga.population[i]) - ga.mutation_individual_impl(ga.population[-i-1]) - - - class Individual: - """Methods for mutating a single chromosome.""" - - @_check_gene_mutation_rate - @_reset_fitness - @_loop_random_mutations - def individual_genes(ga, chromosome, index): - """Mutates random genes by making completely new genes.""" - - # 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 average(ga, chromosome, index): - """Mutates random genes by making completely new genes - and then averaging them with the old genes. May cause - premature convergence. Weight is the reciprocal of the - number of generations run.""" - - weight = 1/max(1, ga.current_generation) - - # Using the chromosome_impl - if ga.chromosome_impl is not None: - new_value = ga.chromosome_impl()[index] - - # Using the gene_impl - elif ga.gene_impl is not None: - new_value = ga.gene_impl() - - # Exit because no gene creation method specified - else: - raise Exception("Did not specify any initialization constraints.") - - chromosome[index] = ga.make_gene((1-weight)*chromosome[index].value + weight*new_value) - - - @_check_gene_mutation_rate - @_reset_fitness - @_loop_random_mutations - def reflect_genes(ga, chromosome, index): - """Reflects genes against the best chromosome. - Requires large genetic variety to work well but - when it does it may be very fast.""" - - 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] - - - @_check_gene_mutation_rate - @_reset_fitness - def swap_segments(ga, chromosome): - """Splits the chromosome into 3 segments and shuffle them.""" - - # Chromosome too short to mutate - if len(chromosome) < 3: - return - - # Indexes to split the chromosome - index_two = random.randrange(2, len(chromosome)) - index_one = random.randrange(1, index_two) - - # Extract segments and shuffle them - segments = [chromosome[:index_one], chromosome[index_one:index_two], chromosome[index_two:]] - random.shuffle(segments) - - # Put segments back together - chromosome.gene_list = segments[0] + segments[1] + segments[2] diff --git a/src/mutation/test_mutation_methods.py b/src/mutation/test_mutation_methods.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/parent_selection/README.md b/src/parent_selection/README.md deleted file mode 100644 index 98253e9..0000000 --- a/src/parent_selection/README.md +++ /dev/null @@ -1 +0,0 @@ -# Selection functions diff --git a/src/parent_selection/__init__.py b/src/parent_selection/__init__.py deleted file mode 100644 index a3d76ba..0000000 --- a/src/parent_selection/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# FROM (. means local) file_name IMPORT function_name -from .parent_selection_methods import Parent_Selection diff --git a/src/parent_selection/parent_selection_methods.py b/src/parent_selection/parent_selection_methods.py deleted file mode 100644 index 0310ae6..0000000 --- a/src/parent_selection/parent_selection_methods.py +++ /dev/null @@ -1,266 +0,0 @@ -from EasyGA import function_info -import random - - -@function_info -def _check_selection_probability(selection_method): - """Raises an exception if the selection_probability - is not between 0 and 1 inclusively. Otherwise runs - the selection method. - """ - - 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.") - - new_method.__name__ = selection_method.__name__ - return new_method - - -@function_info -def _check_positive_fitness(selection_method): - """Raises an exception if the population contains a - chromosome with negative fitness. Otherwise runs - the selection method. - """ - - 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 can't have negative values or be all 0. Consider using rank selection or stochastic selection instead.") - - new_method.__name__ = selection_method.__name__ - return new_method - - -@function_info -def _ensure_sorted(selection_method): - """Sorts the population by fitness - and then runs the selection method. - """ - - def new_method(ga): - ga.population.sort_by_best_fitness(ga) - selection_method(ga) - - new_method.__name__ = selection_method.__name__ - return new_method - - -@function_info -def _compute_parent_amount(selection_method): - """Computes the amount of parents - needed to be selected, and passes it - as another argument for the method. - """ - - def new_method(ga): - parent_amount = max(2, round(len(ga.population)*ga.parent_ratio)) - selection_method(ga, parent_amount) - - new_method.__name__ = selection_method.__name__ - return new_method - - -class Parent_Selection: - - # Allowing access to decorators when importing class - _check_selection_probability = _check_selection_probability - _check_positive_fitness = _check_positive_fitness - _ensure_sorted = _ensure_sorted - _compute_parent_amount = _compute_parent_amount - - - class Rank: - """Methods for selecting parents based on their rankings in the population - i.e. the n-th best chromosome has a fixed probability of being selected, - regardless of their chances""" - - @_check_selection_probability - @_ensure_sorted - @_compute_parent_amount - def tournament(ga, parent_amount): - """ - Will make tournaments of size tournament_size and choose the winner (best fitness) - from the tournament and use it as a parent for the next generation. The total number - of parents selected is determined by parent_ratio, an attribute to the GA object. - May require many loops if the selection probability is very low. - """ - - # Choose the tournament size. - # Use no less than 5 chromosomes per tournament. - tournament_size = int(len(ga.population)*ga.tournament_size_ratio) - if tournament_size < 5: - tournament_size = min(5, len(ga.population)) - - # Repeat tournaments until the mating pool is large enough. - while len(ga.population.mating_pool) < parent_amount: - - # Generate a random tournament group and sort by fitness. - tournament_group = sorted(random.sample( - range(len(ga.population)), - tournament_size - )) - - # For each chromosome, add it to the mating pool based on its rank in the tournament. - for index in range(tournament_size): - - # Probability required is selection_probability * (1-selection_probability) ^ index - # Each chromosome is (1-selection_probability) times - # more likely to become a parent than the next ranked. - if random.random() < ga.selection_probability * (1-ga.selection_probability) ** index: - break - - # Use random in tournament if noone wins - else: - index = random.randrange(tournament_size) - - ga.population.set_parent(tournament_group[index]) - - - @_check_selection_probability - @_ensure_sorted - @_compute_parent_amount - def stochastic_geometric(ga, parent_amount): - """ - Selects parents with probabilities given by a geometric progression. This - method is similar to tournament selection, but doesn't create several - tournaments. Instead, it assigns probabilities to each rank and selects - the entire mating pool using random.choices. Since it essentially uses the - entire population as a tournament repeatedly, it is less likely to select - worse parents than tournament selection. - """ - - # Set the weights of each parent based on their rank. - # Each chromosome is (1-selection_probability) times - # more likely to become a parent than the next ranked. - weights = [ - (1-ga.selection_probability) ** i - for i - in range(len(ga.population)) - ] - - # Set the mating pool. - ga.population.mating_pool = random.choices(ga.population, weights, k = parent_amount) - - - @_check_selection_probability - @_ensure_sorted - @_compute_parent_amount - def stochastic_arithmetic(ga, parent_amount): - """ - Selects parents with probabilities given by an arithmetic progression. This - method is similar to stochastic-geometric selection, but is more likely to - select worse parents with its simpler selection scheme. - """ - - # Set the weights of each parent based on their rank. - # The worst chromosome has a weight of 1, - # the next worst chromosome has a weight of 2, - # etc. - # with an inflation of (1-selection probability) * average weight - - average_weight = (len(ga.population)+1) // 2 - inflation = (1-ga.selection_probability) * average_weight - - weights = [ - i + inflation - for i - in range(len(ga.population), 0, -1) - ] - - # Set the mating pool. - ga.population.mating_pool = random.choices(ga.population, weights, k = parent_amount) - - - class Fitness: - - @_check_selection_probability - @_ensure_sorted - @_check_positive_fitness - @_compute_parent_amount - def roulette(ga, parent_amount): - """Roulette selection works based off of how strong the fitness is of the - chromosomes in the population. The stronger the fitness the higher the probability - that it will be selected. Using the example of a casino roulette wheel. - Where the chromosomes are the numbers to be selected and the board size for - those numbers are directly proportional to the chromosome's current fitness. Where - the ball falls is a randomly generated number between 0 and 1. - """ - - # The sum of all the fitnessess in a 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] - - # The chance of being selected increases incrementally - for index in range(len(ga.population)): - probability.append(probability[-1]+ga.get_chromosome_fitness(index)/fitness_sum) - - probability = probability[1:] - - # Loops until it reaches a desired mating pool size - while len(ga.population.mating_pool) < parent_amount: - - # Spin the roulette - rand_number = random.random() - - # Find where the roulette landed. - for index in range(len(probability)): - if (probability[index] >= rand_number): - ga.population.set_parent(index) - break - - - @_check_selection_probability - @_ensure_sorted - @_compute_parent_amount - def stochastic(ga, parent_amount): - """ - Selects parents using the same probability approach as roulette selection, - but doesn't spin a roulette for every selection. Uses random.choices with - weighted values to select parents and may produce duplicate parents. - """ - - # All fitnesses are the same, select randomly. - if ga.get_chromosome_fitness(-1) == ga.get_chromosome_fitness(0): - offset = 1-ga.get_chromosome_fitness(-1) - - # Some chromosomes have negative fitness, shift them all into positives. - elif ga.get_chromosome_fitness(-1) < 0: - offset = -ga.get_chromosome_fitness(-1) - - # No change needed. - else: - offset = 0 - - # Set the weights of each parent based on their fitness + offset. - weights = [ - ga.get_chromosome_fitness(index) + offset - for index - in range(len(ga.population)) - ] - - inflation = sum(weights) * (1 - ga.selection_probability) - - # Rescale and adjust using selection_probability so that - # if selection_probability is high, a low inflation is used, - # making selection mostly based on fitness. - # if selection_probability is low, a high offset is used, - # so everyone has a more equal chance. - weights = [ - weight + inflation - for weight - in weights - ] - - # Set the mating pool. - ga.population.mating_pool = random.choices(ga.population, weights, k = parent_amount) diff --git a/src/parent_selection/test_parent_selection_methods.py b/src/parent_selection/test_parent_selection_methods.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/survivor_selection/README.md b/src/survivor_selection/README.md deleted file mode 100644 index 98253e9..0000000 --- a/src/survivor_selection/README.md +++ /dev/null @@ -1 +0,0 @@ -# Selection functions diff --git a/src/survivor_selection/__init__.py b/src/survivor_selection/__init__.py deleted file mode 100644 index b6806fa..0000000 --- a/src/survivor_selection/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# FROM (. means local) file_name IMPORT function_name -from .survivor_selection_methods import Survivor_Selection diff --git a/src/survivor_selection/survivor_selection_methods.py b/src/survivor_selection/survivor_selection_methods.py deleted file mode 100644 index 995f435..0000000 --- a/src/survivor_selection/survivor_selection_methods.py +++ /dev/null @@ -1,60 +0,0 @@ -from EasyGA import function_info -import random - - -@function_info -def _append_to_next_population(survivor_method): - """Appends the selected chromosomes to the next population.""" - - def new_method(ga): - ga.population.append_children(survivor_method(ga)) - - new_method.__name__ = survivor_method.__name__ - return new_method - - -class Survivor_Selection: - """Survivor selection determines which individuals should be brought to the next generation""" - - # Allowing access to decorators when importing class - _append_to_next_population = _append_to_next_population - - - @_append_to_next_population - def fill_in_best(ga): - """Fills in the next population with the best chromosomes from the last population""" - - needed_amount = len(ga.population) - len(ga.population.next_population) - return ga.population[:needed_amount] - - - @_append_to_next_population - def fill_in_random(ga): - """Fills in the next population with random chromosomes from the last population""" - - needed_amount = len(ga.population) - len(ga.population.next_population) - return random.sample(ga.population, needed_amount) - - - @_append_to_next_population - 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(needed_amount, len(mating_pool)) - random_amount = needed_amount - parent_amount - - # Only parents are used. - if random_amount == 0: - return ga.population.mating_pool[:parent_amount] - - # Parents need to be removed from the random sample to avoid dupes. - else: - return mating_pool \ - + random.sample( - set(ga.population) - mating_pool, - random_amount - ) diff --git a/src/survivor_selection/test_survivor_methods.py b/src/survivor_selection/test_survivor_methods.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/termination_point/README.md b/src/termination_point/README.md deleted file mode 100644 index 7b74650..0000000 --- a/src/termination_point/README.md +++ /dev/null @@ -1 +0,0 @@ -# Termination functions diff --git a/src/termination_point/__init__.py b/src/termination_point/__init__.py deleted file mode 100644 index 9eb2097..0000000 --- a/src/termination_point/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# FROM (. means local) file_name IMPORT class name -from .termination_methods import Termination_Methods diff --git a/src/termination_point/termination_methods.py b/src/termination_point/termination_methods.py deleted file mode 100644 index 3776f67..0000000 --- a/src/termination_point/termination_methods.py +++ /dev/null @@ -1,99 +0,0 @@ -from EasyGA import function_info - -@function_info -def _add_by_fitness_goal(termination_impl): - """Adds termination by fitness goal to the method.""" - - def new_method(ga): - - # Try to check the fitness goal - try: - - # If minimum fitness goal reached, stop ga. - if ga.target_fitness_type == 'min' and ga.population[0].fitness <= ga.fitness_goal: - return False - - # If maximum fitness goal reached, stop ga. - elif ga.target_fitness_type == 'max' and ga.population[0].fitness >= ga.fitness_goal: - return False - - # Fitness or fitness goals are None - except TypeError: - pass - - # Population not initialized - except AttributeError: - pass - - # Check other termination methods - return termination_impl(ga) - - return new_method - - -@function_info -def _add_by_generation_goal(termination_impl): - """Adds termination by generation goal to the method.""" - - 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: - return False - - # Check other termination methods - return termination_impl(ga) - - return new_method - - -@function_info -def _add_by_tolerance_goal(termination_impl): - """Adds termination by tolerance goal to the method.""" - - def new_method(ga): - - # If tolerance is set, check it, if possible. - try: - best_fitness = ga.population[0].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 - if abs(best_fitness - threshhold_fitness) < tol: - return False - - # Fitness or tolerance goals are None - except TypeError: - pass - - # Population not initialized - except AttributeError: - pass - - # Check other termination methods - return termination_impl(ga) - - return new_method - - -class Termination_Methods: - """Example functions that can be used to terminate the the algorithms loop""" - - # Allowing access to decorators when importing class - _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 - @_add_by_generation_goal - @_add_by_tolerance_goal - def fitness_generation_tolerance(ga): - """Terminate GA when any of the - - fitness, - - generation, or - - tolerance - goals are met.""" - - return True diff --git a/src/termination_point/test_termination_methods.py b/src/termination_point/test_termination_methods.py deleted file mode 100644 index e69de29..0000000