From 39be73f0b5334cddc81c8d4cca597f52a4d97cd3 Mon Sep 17 00:00:00 2001 From: UTSAV SINGHAL <119779889+UTSAVS26@users.noreply.github.com> Date: Fri, 15 Nov 2024 14:23:56 +0530 Subject: [PATCH 1/8] Create genetic_algorithm_optimization.py --- .../genetic_algorithm_optimization.py | 214 ++++++++++++++++++ 1 file changed, 214 insertions(+) create mode 100644 genetic_algorithm/genetic_algorithm_optimization.py diff --git a/genetic_algorithm/genetic_algorithm_optimization.py b/genetic_algorithm/genetic_algorithm_optimization.py new file mode 100644 index 000000000000..d4c9f02bc628 --- /dev/null +++ b/genetic_algorithm/genetic_algorithm_optimization.py @@ -0,0 +1,214 @@ +import random +from collections.abc import Callable, Sequence +from concurrent.futures import ThreadPoolExecutor + +import numpy as np + +# Parameters +N_POPULATION = 100 # Population size +N_GENERATIONS = 500 # Maximum number of generations +N_SELECTED = 50 # Number of parents selected for the next generation +MUTATION_PROBABILITY = 0.1 # Mutation probability +CROSSOVER_RATE = 0.8 # Probability of crossover +SEARCH_SPACE = (-10, 10) # Search space for the variables + +# Random number generator +rng = np.random.default_rng() + + +class GeneticAlgorithm: + def __init__( + self, + function: Callable[[float, float], float], + bounds: Sequence[tuple[int | float, int | float]], + population_size: int, + generations: int, + mutation_prob: float, + crossover_rate: float, + maximize: bool = True, + ) -> None: + self.function = function # Target function to optimize + self.bounds = bounds # Search space bounds (for each variable) + self.population_size = population_size + self.generations = generations + self.mutation_prob = mutation_prob + self.crossover_rate = crossover_rate + self.maximize = maximize + self.dim = len(bounds) # Dimensionality of the function (number of variables) + + # Initialize population + self.population = self.initialize_population() + + def initialize_population(self) -> list[np.ndarray]: + """Initialize the population with random individuals within the search space.""" + return [ + rng.uniform( + low=[self.bounds[j][0] for j in range(self.dim)], + high=[self.bounds[j][1] for j in range(self.dim)], + ) + for _ in range(self.population_size) + ] + + def fitness(self, individual: np.ndarray) -> float: + """Calculate the fitness value (function value) for an individual.""" + value = float(self.function(*individual)) # Ensure fitness is a float + return value if self.maximize else -value # If minimizing, invert the fitness + + def select_parents( + self, population_score: list[tuple[np.ndarray, float]] + ) -> list[np.ndarray]: + """Select top N_SELECTED parents based on fitness.""" + population_score.sort(key=lambda score_tuple: score_tuple[1], reverse=True) + selected_count = min(N_SELECTED, len(population_score)) + return [ind for ind, _ in population_score[:selected_count]] + + def crossover( + self, parent1: np.ndarray, parent2: np.ndarray + ) -> tuple[np.ndarray, np.ndarray]: + """ + Perform uniform crossover between two parents to generate offspring. + Args: + parent1 (np.ndarray): The first parent. + parent2 (np.ndarray): The second parent. + Returns: + tuple[np.ndarray, np.ndarray]: The two offspring generated by crossover. + Example: + >>> ga = GeneticAlgorithm( + ... lambda x, y: -(x**2 + y**2), + ... [(-10, 10), (-10, 10)], + ... 10, 100, 0.1, 0.8, True + ... ) + >>> parent1, parent2 = np.array([1, 2]), np.array([3, 4]) + >>> len(ga.crossover(parent1, parent2)) == 2 + True + """ + if random.random() < self.crossover_rate: + cross_point = random.randint(1, self.dim - 1) + child1 = np.concatenate((parent1[:cross_point], parent2[cross_point:])) + child2 = np.concatenate((parent2[:cross_point], parent1[cross_point:])) + return child1, child2 + return parent1, parent2 + + def mutate(self, individual: np.ndarray) -> np.ndarray: + """ + Apply mutation to an individual. + Args: + individual (np.ndarray): The individual to mutate. + Returns: + np.ndarray: The mutated individual. + Example: + >>> ga = GeneticAlgorithm( + ... lambda x, y: -(x**2 + y**2), + ... [(-10, 10), (-10, 10)], + ... 10, 100, 0.1, 0.8, True + ... ) + >>> ind = np.array([1.0, 2.0]) + >>> mutated = ga.mutate(ind) + >>> len(mutated) == 2 # Ensure it still has the correct number of dimensions + True + """ + for i in range(self.dim): + if random.random() < self.mutation_prob: + individual[i] = rng.uniform(self.bounds[i][0], self.bounds[i][1]) + return individual + + def evaluate_population(self) -> list[tuple[np.ndarray, float]]: + """ + Evaluate the fitness of the entire population in parallel. + Returns: + list[tuple[np.ndarray, float]]: + The population with their respective fitness values. + Example: + >>> ga = GeneticAlgorithm( + ... lambda x, y: -(x**2 + y**2), + ... [(-10, 10), (-10, 10)], + ... 10, 100, 0.1, 0.8, True + ... ) + >>> eval_population = ga.evaluate_population() + >>> len(eval_population) == ga.population_size # Ensure population size + True + >>> all( + ... isinstance(ind, tuple) and isinstance(ind[1], float) + ... for ind in eval_population + ... ) + True + """ + with ThreadPoolExecutor() as executor: + return list( + executor.map( + lambda individual: (individual, self.fitness(individual)), + self.population, + ) + ) + + def evolve(self, verbose=True) -> np.ndarray: + """ + Evolve the population over the generations to find the best solution. + Returns: + np.ndarray: The best individual found during the evolution process. + """ + for generation in range(self.generations): + # Evaluate population fitness (multithreaded) + population_score = self.evaluate_population() + + # Check the best individual + best_individual = max( + population_score, key=lambda score_tuple: score_tuple[1] + )[0] + best_fitness = self.fitness(best_individual) + + # Select parents for next generation + parents = self.select_parents(population_score) + next_generation = [] + + # Generate offspring using crossover and mutation + for i in range(0, len(parents), 2): + parent1, parent2 = parents[i], parents[(i + 1) % len(parents)] + child1, child2 = self.crossover(parent1, parent2) + next_generation.append(self.mutate(child1)) + next_generation.append(self.mutate(child2)) + + # Ensure population size remains the same + self.population = next_generation[: self.population_size] + + if verbose and generation % 10 == 0: + print(f"Generation {generation}: Best Fitness = {best_fitness}") + + return best_individual + + +# Example target function for optimization +def target_function(var_x: float, var_y: float) -> float: + """ + Example target function (parabola) for optimization. + Args: + var_x (float): The x-coordinate. + var_y (float): The y-coordinate. + Returns: + float: The value of the function at (var_x, var_y). + Example: + >>> target_function(0, 0) + 0 + >>> target_function(1, 1) + 2 + """ + return var_x**2 + var_y**2 # Simple parabolic surface (minimization) + + +# Set bounds for the variables (var_x, var_y) +bounds = [(-10, 10), (-10, 10)] # Both var_x and var_y range from -10 to 10 + +# Instantiate and run the genetic algorithm +ga = GeneticAlgorithm( + function=target_function, + bounds=bounds, + population_size=N_POPULATION, + generations=N_GENERATIONS, + mutation_prob=MUTATION_PROBABILITY, + crossover_rate=CROSSOVER_RATE, + maximize=False, # Minimize the function +) + +best_solution = ga.evolve() +print(f"Best solution found: {best_solution}") +print(f"Best fitness (minimum value of function): {target_function(*best_solution)}") From c9c9639803fb47bd2de77149e7708c1846dd2c75 Mon Sep 17 00:00:00 2001 From: UTSAV SINGHAL <119779889+UTSAVS26@users.noreply.github.com> Date: Fri, 15 Nov 2024 14:29:51 +0530 Subject: [PATCH 2/8] Update genetic_algorithm_optimization.py --- .../genetic_algorithm_optimization.py | 101 +++++++++++++++++- 1 file changed, 96 insertions(+), 5 deletions(-) diff --git a/genetic_algorithm/genetic_algorithm_optimization.py b/genetic_algorithm/genetic_algorithm_optimization.py index d4c9f02bc628..9cb059553d1d 100644 --- a/genetic_algorithm/genetic_algorithm_optimization.py +++ b/genetic_algorithm/genetic_algorithm_optimization.py @@ -1,7 +1,6 @@ import random from collections.abc import Callable, Sequence from concurrent.futures import ThreadPoolExecutor - import numpy as np # Parameters @@ -40,7 +39,25 @@ def __init__( self.population = self.initialize_population() def initialize_population(self) -> list[np.ndarray]: - """Initialize the population with random individuals within the search space.""" + """ + Initialize the population with random individuals within the search space. + + Example: + >>> ga = GeneticAlgorithm( + ... function=lambda x, y: x**2 + y**2, + ... bounds=[(-10, 10), (-10, 10)], + ... population_size=5, + ... generations=10, + ... mutation_prob=0.1, + ... crossover_rate=0.8, + ... maximize=False + ... ) + >>> len(ga.initialize_population()) + 5 # The population size should be equal to 5. + >>> all(len(ind) == 2 for ind in ga.initialize_population()) + # Each individual should have 2 variables + True + """ return [ rng.uniform( low=[self.bounds[j][0] for j in range(self.dim)], @@ -50,14 +67,58 @@ def initialize_population(self) -> list[np.ndarray]: ] def fitness(self, individual: np.ndarray) -> float: - """Calculate the fitness value (function value) for an individual.""" + """ + Calculate the fitness value (function value) for an individual. + + Example: + >>> ga = GeneticAlgorithm( + ... function=lambda x, y: x**2 + y**2, + ... bounds=[(-10, 10), (-10, 10)], + ... population_size=10, + ... generations=10, + ... mutation_prob=0.1, + ... crossover_rate=0.8, + ... maximize=False + ... ) + >>> individual = np.array([1.0, 2.0]) + >>> ga.fitness(individual) + 5.0 # The fitness should be 1^2 + 2^2 = 5 + >>> ga.maximize = True + >>> ga.fitness(individual) + -5.0 # The fitness should be -5 when maximizing + """ value = float(self.function(*individual)) # Ensure fitness is a float return value if self.maximize else -value # If minimizing, invert the fitness def select_parents( self, population_score: list[tuple[np.ndarray, float]] ) -> list[np.ndarray]: - """Select top N_SELECTED parents based on fitness.""" + """ + Select top N_SELECTED parents based on fitness. + + Example: + >>> ga = GeneticAlgorithm( + ... function=lambda x, y: x**2 + y**2, + ... bounds=[(-10, 10), (-10, 10)], + ... population_size=10, + ... generations=10, + ... mutation_prob=0.1, + ... crossover_rate=0.8, + ... maximize=False + ... ) + >>> population_score = [ + ... (np.array([1.0, 2.0]), 5.0), + ... (np.array([-1.0, -2.0]), 5.0), + ... (np.array([0.0, 0.0]), 0.0), + ... ] + >>> selected_parents = ga.select_parents(population_score) + >>> len(selected_parents) + 2 # Should select the two parents with the best fitness scores. + >>> np.array_equal(selected_parents[0], np.array([1.0, 2.0])) # Parent 1 should be [1.0, 2.0] + True + >>> np.array_equal(selected_parents[1], np.array([-1.0, -2.0])) # Parent 2 should be [-1.0, -2.0] + True + """ population_score.sort(key=lambda score_tuple: score_tuple[1], reverse=True) selected_count = min(N_SELECTED, len(population_score)) return [ind for ind, _ in population_score[:selected_count]] @@ -67,11 +128,13 @@ def crossover( ) -> tuple[np.ndarray, np.ndarray]: """ Perform uniform crossover between two parents to generate offspring. + Args: parent1 (np.ndarray): The first parent. parent2 (np.ndarray): The second parent. Returns: tuple[np.ndarray, np.ndarray]: The two offspring generated by crossover. + Example: >>> ga = GeneticAlgorithm( ... lambda x, y: -(x**2 + y**2), @@ -92,10 +155,13 @@ def crossover( def mutate(self, individual: np.ndarray) -> np.ndarray: """ Apply mutation to an individual. + Args: individual (np.ndarray): The individual to mutate. + Returns: np.ndarray: The mutated individual. + Example: >>> ga = GeneticAlgorithm( ... lambda x, y: -(x**2 + y**2), @@ -115,9 +181,11 @@ def mutate(self, individual: np.ndarray) -> np.ndarray: def evaluate_population(self) -> list[tuple[np.ndarray, float]]: """ Evaluate the fitness of the entire population in parallel. + Returns: list[tuple[np.ndarray, float]]: The population with their respective fitness values. + Example: >>> ga = GeneticAlgorithm( ... lambda x, y: -(x**2 + y**2), @@ -141,11 +209,33 @@ def evaluate_population(self) -> list[tuple[np.ndarray, float]]: ) ) - def evolve(self, verbose=True) -> np.ndarray: + def evolve(self, verbose: bool = True) -> np.ndarray: """ Evolve the population over the generations to find the best solution. + + Args: + verbose (bool): If True, prints the progress of the generations. + Returns: np.ndarray: The best individual found during the evolution process. + + Example: + >>> ga = GeneticAlgorithm( + ... function=lambda x, y: x**2 + y**2, + ... bounds=[(-10, 10), (-10, 10)], + ... population_size=10, + ... generations=10, + ... mutation_prob=0.1, + ... crossover_rate=0.8, + ... maximize=False + ... ) + >>> best_solution = ga.evolve(verbose=False) + >>> len(best_solution) + 2 # The best solution should be a 2-element array (var_x, var_y) + >>> isinstance(best_solution[0], float) # First element should be a float + True + >>> isinstance(best_solution[1], float) # Second element should be a float + True """ for generation in range(self.generations): # Evaluate population fitness (multithreaded) @@ -186,6 +276,7 @@ def target_function(var_x: float, var_y: float) -> float: var_y (float): The y-coordinate. Returns: float: The value of the function at (var_x, var_y). + Example: >>> target_function(0, 0) 0 From d62f39f6472f668df3a4a289e2fe0df64c1bd769 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 15 Nov 2024 09:01:01 +0000 Subject: [PATCH 3/8] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- genetic_algorithm/genetic_algorithm_optimization.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/genetic_algorithm/genetic_algorithm_optimization.py b/genetic_algorithm/genetic_algorithm_optimization.py index 9cb059553d1d..23cc9bdf3b13 100644 --- a/genetic_algorithm/genetic_algorithm_optimization.py +++ b/genetic_algorithm/genetic_algorithm_optimization.py @@ -54,7 +54,7 @@ def initialize_population(self) -> list[np.ndarray]: ... ) >>> len(ga.initialize_population()) 5 # The population size should be equal to 5. - >>> all(len(ind) == 2 for ind in ga.initialize_population()) + >>> all(len(ind) == 2 for ind in ga.initialize_population()) # Each individual should have 2 variables True """ @@ -134,7 +134,7 @@ def crossover( parent2 (np.ndarray): The second parent. Returns: tuple[np.ndarray, np.ndarray]: The two offspring generated by crossover. - + Example: >>> ga = GeneticAlgorithm( ... lambda x, y: -(x**2 + y**2), @@ -276,7 +276,7 @@ def target_function(var_x: float, var_y: float) -> float: var_y (float): The y-coordinate. Returns: float: The value of the function at (var_x, var_y). - + Example: >>> target_function(0, 0) 0 From dbd29aed7653821ed3773612a876ff9dc1645b53 Mon Sep 17 00:00:00 2001 From: UTSAV SINGHAL <119779889+UTSAVS26@users.noreply.github.com> Date: Fri, 15 Nov 2024 14:50:15 +0530 Subject: [PATCH 4/8] Update genetic_algorithm_optimization.py --- genetic_algorithm/genetic_algorithm_optimization.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/genetic_algorithm/genetic_algorithm_optimization.py b/genetic_algorithm/genetic_algorithm_optimization.py index 23cc9bdf3b13..6a1020aa7f3d 100644 --- a/genetic_algorithm/genetic_algorithm_optimization.py +++ b/genetic_algorithm/genetic_algorithm_optimization.py @@ -1,6 +1,7 @@ import random from collections.abc import Callable, Sequence from concurrent.futures import ThreadPoolExecutor + import numpy as np # Parameters @@ -82,10 +83,10 @@ def fitness(self, individual: np.ndarray) -> float: ... ) >>> individual = np.array([1.0, 2.0]) >>> ga.fitness(individual) - 5.0 # The fitness should be 1^2 + 2^2 = 5 + -5.0 # The fitness should be -1^2 + 2^2 = 5 for minimizing >>> ga.maximize = True >>> ga.fitness(individual) - -5.0 # The fitness should be -5 when maximizing + 5.0 # The fitness should be 1^2 + 2^2 = 5 when maximizing """ value = float(self.function(*individual)) # Ensure fitness is a float return value if self.maximize else -value # If minimizing, invert the fitness @@ -114,9 +115,11 @@ def select_parents( >>> selected_parents = ga.select_parents(population_score) >>> len(selected_parents) 2 # Should select the two parents with the best fitness scores. - >>> np.array_equal(selected_parents[0], np.array([1.0, 2.0])) # Parent 1 should be [1.0, 2.0] + >>> np.array_equal(selected_parents[0], np.array([1.0, 2.0])) + # Parent 1 should be [1.0, 2.0] True - >>> np.array_equal(selected_parents[1], np.array([-1.0, -2.0])) # Parent 2 should be [-1.0, -2.0] + >>> np.array_equal(selected_parents[1], np.array([-1.0, -2.0])) + # Parent 2 should be [-1.0, -2.0] True """ population_score.sort(key=lambda score_tuple: score_tuple[1], reverse=True) @@ -237,6 +240,7 @@ def evolve(self, verbose: bool = True) -> np.ndarray: >>> isinstance(best_solution[1], float) # Second element should be a float True """ + best_individual = None for generation in range(self.generations): # Evaluate population fitness (multithreaded) population_score = self.evaluate_population() From 84b29c0eed975f3dd891cb35c11f240a873a424a Mon Sep 17 00:00:00 2001 From: UTSAV SINGHAL <119779889+UTSAVS26@users.noreply.github.com> Date: Fri, 15 Nov 2024 14:56:23 +0530 Subject: [PATCH 5/8] Update genetic_algorithm_optimization.py --- .../genetic_algorithm_optimization.py | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/genetic_algorithm/genetic_algorithm_optimization.py b/genetic_algorithm/genetic_algorithm_optimization.py index 6a1020aa7f3d..813ac72e8dd0 100644 --- a/genetic_algorithm/genetic_algorithm_optimization.py +++ b/genetic_algorithm/genetic_algorithm_optimization.py @@ -60,10 +60,7 @@ def initialize_population(self) -> list[np.ndarray]: True """ return [ - rng.uniform( - low=[self.bounds[j][0] for j in range(self.dim)], - high=[self.bounds[j][1] for j in range(self.dim)], - ) + np.array([rng.uniform(b[0], b[1]) for b in self.bounds]) for _ in range(self.population_size) ] @@ -122,6 +119,10 @@ def select_parents( # Parent 2 should be [-1.0, -2.0] True """ + + if not population_score: + raise ValueError("Population score is empty, cannot select parents.") + population_score.sort(key=lambda score_tuple: score_tuple[1], reverse=True) selected_count = min(N_SELECTED, len(population_score)) return [ind for ind, _ in population_score[:selected_count]] @@ -244,30 +245,32 @@ def evolve(self, verbose: bool = True) -> np.ndarray: for generation in range(self.generations): # Evaluate population fitness (multithreaded) population_score = self.evaluate_population() - + + # Ensure population_score isn't empty + if not population_score: + raise ValueError("Population score is empty. No individuals evaluated.") + # Check the best individual - best_individual = max( - population_score, key=lambda score_tuple: score_tuple[1] - )[0] + best_individual = max(population_score, key=lambda score_tuple: score_tuple[1])[0] best_fitness = self.fitness(best_individual) - + # Select parents for next generation parents = self.select_parents(population_score) next_generation = [] - + # Generate offspring using crossover and mutation for i in range(0, len(parents), 2): - parent1, parent2 = parents[i], parents[(i + 1) % len(parents)] + parent1, parent2 = parents[i], parents[(i + 1) % len(parents)] # Wrap around for odd cases child1, child2 = self.crossover(parent1, parent2) next_generation.append(self.mutate(child1)) next_generation.append(self.mutate(child2)) - + # Ensure population size remains the same self.population = next_generation[: self.population_size] - + if verbose and generation % 10 == 0: print(f"Generation {generation}: Best Fitness = {best_fitness}") - + return best_individual From 9049228ff8b32a3b30f80b91939132dfb4636cc3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 15 Nov 2024 09:26:56 +0000 Subject: [PATCH 6/8] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../genetic_algorithm_optimization.py | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/genetic_algorithm/genetic_algorithm_optimization.py b/genetic_algorithm/genetic_algorithm_optimization.py index 813ac72e8dd0..66227ca21bb1 100644 --- a/genetic_algorithm/genetic_algorithm_optimization.py +++ b/genetic_algorithm/genetic_algorithm_optimization.py @@ -122,7 +122,7 @@ def select_parents( if not population_score: raise ValueError("Population score is empty, cannot select parents.") - + population_score.sort(key=lambda score_tuple: score_tuple[1], reverse=True) selected_count = min(N_SELECTED, len(population_score)) return [ind for ind, _ in population_score[:selected_count]] @@ -245,32 +245,37 @@ def evolve(self, verbose: bool = True) -> np.ndarray: for generation in range(self.generations): # Evaluate population fitness (multithreaded) population_score = self.evaluate_population() - + # Ensure population_score isn't empty if not population_score: raise ValueError("Population score is empty. No individuals evaluated.") - + # Check the best individual - best_individual = max(population_score, key=lambda score_tuple: score_tuple[1])[0] + best_individual = max( + population_score, key=lambda score_tuple: score_tuple[1] + )[0] best_fitness = self.fitness(best_individual) - + # Select parents for next generation parents = self.select_parents(population_score) next_generation = [] - + # Generate offspring using crossover and mutation for i in range(0, len(parents), 2): - parent1, parent2 = parents[i], parents[(i + 1) % len(parents)] # Wrap around for odd cases + parent1, parent2 = ( + parents[i], + parents[(i + 1) % len(parents)], + ) # Wrap around for odd cases child1, child2 = self.crossover(parent1, parent2) next_generation.append(self.mutate(child1)) next_generation.append(self.mutate(child2)) - + # Ensure population size remains the same self.population = next_generation[: self.population_size] - + if verbose and generation % 10 == 0: print(f"Generation {generation}: Best Fitness = {best_fitness}") - + return best_individual From 20184aa4338272641a2b3321f3d433889ae5d148 Mon Sep 17 00:00:00 2001 From: UTSAV SINGHAL <119779889+UTSAVS26@users.noreply.github.com> Date: Mon, 2 Dec 2024 14:22:39 +0530 Subject: [PATCH 7/8] Update genetic_algorithm_optimization.py doctest for the function select_parents --- .../genetic_algorithm_optimization.py | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/genetic_algorithm/genetic_algorithm_optimization.py b/genetic_algorithm/genetic_algorithm_optimization.py index 66227ca21bb1..8c04b3ad2fa8 100644 --- a/genetic_algorithm/genetic_algorithm_optimization.py +++ b/genetic_algorithm/genetic_algorithm_optimization.py @@ -113,13 +113,24 @@ def select_parents( >>> len(selected_parents) 2 # Should select the two parents with the best fitness scores. >>> np.array_equal(selected_parents[0], np.array([1.0, 2.0])) - # Parent 1 should be [1.0, 2.0] - True + True # Parent 1 should be [1.0, 2.0] >>> np.array_equal(selected_parents[1], np.array([-1.0, -2.0])) - # Parent 2 should be [-1.0, -2.0] - True - """ + True # Parent 2 should be [-1.0, -2.0] + >>> population_score = [ + ... (np.array([1.0, 2.0]), 5.0), + ... (np.array([1.0, -2.0]), 5.0), + ... (np.array([0.0, 0.0]), 0.0), + ... (np.array([-1.0, 2.0]), 5.0), + ... (np.array([-1.0, -2.0]), 5.0) + ... ] + >>> selected_parents = ga.select_parents(population_score) + >>> len(selected_parents) + 5 # Should select the top 5 parents with the best fitness scores. + >>> np.array_equal(selected_parents[0], np.array([1.0, 2.0])) + True # Parent 1 should be [1.0, 2.0] + """ + if not population_score: raise ValueError("Population score is empty, cannot select parents.") From 064704be467b97c7e08c1d2aff132dd910f7896c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 2 Dec 2024 08:53:14 +0000 Subject: [PATCH 8/8] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- genetic_algorithm/genetic_algorithm_optimization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genetic_algorithm/genetic_algorithm_optimization.py b/genetic_algorithm/genetic_algorithm_optimization.py index 8c04b3ad2fa8..be0b3d52202d 100644 --- a/genetic_algorithm/genetic_algorithm_optimization.py +++ b/genetic_algorithm/genetic_algorithm_optimization.py @@ -130,7 +130,7 @@ def select_parents( >>> np.array_equal(selected_parents[0], np.array([1.0, 2.0])) True # Parent 1 should be [1.0, 2.0] """ - + if not population_score: raise ValueError("Population score is empty, cannot select parents.")