From 5aa3a976255d03c25fbd40ffcfa592d9611abde9 Mon Sep 17 00:00:00 2001 From: KelvinPuyam Date: Sat, 20 Apr 2024 01:06:01 +0530 Subject: [PATCH 1/4] Add function_optimization.py --- genetic_algorithm/function_optimization.py | 94 ++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 genetic_algorithm/function_optimization.py diff --git a/genetic_algorithm/function_optimization.py b/genetic_algorithm/function_optimization.py new file mode 100644 index 000000000000..09d669486c13 --- /dev/null +++ b/genetic_algorithm/function_optimization.py @@ -0,0 +1,94 @@ +""" +Genetic algorithm for function optimization. + +Author: kpuyam +""" + +import random +from typing import Callable, Tuple, List + +# Define the parameters for the genetic algorithm +N_POPULATION = 100 +N_SELECTED = 20 +MUTATION_PROBABILITY = 0.1 +NUM_GENERATIONS = 50 + +def evaluate(func: Callable, params: List[float]) -> float: + """ + Evaluate the fitness of an individual based on the provided function. + + Example: + >>> evaluate(lambda x, y: x ** 2 + y ** 2, [3, 4]) + 25 + >>> evaluate(lambda x, y: x + y, [3, 4]) + 7 + """ + return func(*params) + +def crossover(parent_1: List[float], parent_2: List[float]) -> List[float]: + """Perform crossover between two parents.""" + crossover_point = random.randint(1, len(parent_1) - 1) + child = parent_1[:crossover_point] + parent_2[crossover_point:] + return child + +def mutate(individual: List[float], mutation_rate: float) -> List[float]: + """Mutate an individual with a certain probability.""" + mutated_individual = [] + for gene in individual: + if random.random() < mutation_rate: + # Add some noise to the gene's value + mutated_gene = gene + random.uniform(-0.1, 0.1) + mutated_individual.append(mutated_gene) + else: + mutated_individual.append(gene) + return mutated_individual + +def select(population: List[Tuple[List[float], float]], num_selected: int) -> List[List[float]]: + """Select individuals based on their fitness scores.""" + sorted_population = sorted(population, key=lambda x: x[1]) + selected_parents = [individual[0] for individual in sorted_population[:num_selected]] + return selected_parents + +def optimize(func: Callable, num_params: int, param_ranges: List[Tuple[float, float]], optimization_goal: str) -> Tuple[List[float], float]: + """Optimize the given function using a genetic algorithm.""" + # Initialize the population + population = [[random.uniform(param_range[0], param_range[1]) for param_range in param_ranges] for _ in range(N_POPULATION)] + + # Main optimization loop + for generation in range(NUM_GENERATIONS): + # Evaluate the fitness of each individual in the population + population_fitness = [(individual, evaluate(func, individual)) for individual in population] + + # Select parents for crossover + selected_parents = select(population_fitness, N_SELECTED) + + # Generate offspring through crossover and mutation + offspring = [] + for i in range(N_POPULATION): + parent_1 = random.choice(selected_parents) + parent_2 = random.choice(selected_parents) + child = crossover(parent_1, parent_2) + child = mutate(child, MUTATION_PROBABILITY) + offspring.append(child) + + # Replace the old population with the offspring + population = offspring + + # Find the best individual in the final population + best_individual, best_fitness = max(population_fitness, key=lambda x: x[1]) + + return best_individual, best_fitness + +if __name__ == "__main__": + # Set random seed for reproducibility + random.seed(123) + + # Example usage: + def quadratic_function(x, y): + """Example function to optimize.""" + return x ** 2 + y ** 2 + + param_ranges = [(-10, 10), (-10, 10)] + best_params, best_fitness = optimize(quadratic_function, 2, param_ranges, "minimize") + print("Best parameters:", best_params) + print("Best fitness:", best_fitness) From 13d0e391118d95fe9725d3ccb0a1f5a5f3c76cad Mon Sep 17 00:00:00 2001 From: KelvinPuyam Date: Sat, 20 Apr 2024 01:09:15 +0530 Subject: [PATCH 2/4] Reformatted function_optimization.py using black --- genetic_algorithm/function_optimization.py | 36 +++++++++++++++++----- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/genetic_algorithm/function_optimization.py b/genetic_algorithm/function_optimization.py index 09d669486c13..5e1fd55b0377 100644 --- a/genetic_algorithm/function_optimization.py +++ b/genetic_algorithm/function_optimization.py @@ -13,6 +13,7 @@ MUTATION_PROBABILITY = 0.1 NUM_GENERATIONS = 50 + def evaluate(func: Callable, params: List[float]) -> float: """ Evaluate the fitness of an individual based on the provided function. @@ -25,12 +26,14 @@ def evaluate(func: Callable, params: List[float]) -> float: """ return func(*params) + def crossover(parent_1: List[float], parent_2: List[float]) -> List[float]: """Perform crossover between two parents.""" crossover_point = random.randint(1, len(parent_1) - 1) child = parent_1[:crossover_point] + parent_2[crossover_point:] return child + def mutate(individual: List[float], mutation_rate: float) -> List[float]: """Mutate an individual with a certain probability.""" mutated_individual = [] @@ -43,21 +46,37 @@ def mutate(individual: List[float], mutation_rate: float) -> List[float]: mutated_individual.append(gene) return mutated_individual -def select(population: List[Tuple[List[float], float]], num_selected: int) -> List[List[float]]: + +def select( + population: List[Tuple[List[float], float]], num_selected: int +) -> List[List[float]]: """Select individuals based on their fitness scores.""" sorted_population = sorted(population, key=lambda x: x[1]) - selected_parents = [individual[0] for individual in sorted_population[:num_selected]] + selected_parents = [ + individual[0] for individual in sorted_population[:num_selected] + ] return selected_parents -def optimize(func: Callable, num_params: int, param_ranges: List[Tuple[float, float]], optimization_goal: str) -> Tuple[List[float], float]: + +def optimize( + func: Callable, + num_params: int, + param_ranges: List[Tuple[float, float]], + optimization_goal: str, +) -> Tuple[List[float], float]: """Optimize the given function using a genetic algorithm.""" # Initialize the population - population = [[random.uniform(param_range[0], param_range[1]) for param_range in param_ranges] for _ in range(N_POPULATION)] + population = [ + [random.uniform(param_range[0], param_range[1]) for param_range in param_ranges] + for _ in range(N_POPULATION) + ] # Main optimization loop for generation in range(NUM_GENERATIONS): # Evaluate the fitness of each individual in the population - population_fitness = [(individual, evaluate(func, individual)) for individual in population] + population_fitness = [ + (individual, evaluate(func, individual)) for individual in population + ] # Select parents for crossover selected_parents = select(population_fitness, N_SELECTED) @@ -79,6 +98,7 @@ def optimize(func: Callable, num_params: int, param_ranges: List[Tuple[float, fl return best_individual, best_fitness + if __name__ == "__main__": # Set random seed for reproducibility random.seed(123) @@ -86,9 +106,11 @@ def optimize(func: Callable, num_params: int, param_ranges: List[Tuple[float, fl # Example usage: def quadratic_function(x, y): """Example function to optimize.""" - return x ** 2 + y ** 2 + return x**2 + y**2 param_ranges = [(-10, 10), (-10, 10)] - best_params, best_fitness = optimize(quadratic_function, 2, param_ranges, "minimize") + best_params, best_fitness = optimize( + quadratic_function, 2, param_ranges, "minimize" + ) print("Best parameters:", best_params) print("Best fitness:", best_fitness) From bbee49db1c05f387a979086406ce568e767e98d2 Mon Sep 17 00:00:00 2001 From: KelvinPuyam Date: Sat, 20 Apr 2024 01:15:17 +0530 Subject: [PATCH 3/4] Refactor functions in function_optimization.py --- genetic_algorithm/function_optimization.py | 26 +++++++++------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/genetic_algorithm/function_optimization.py b/genetic_algorithm/function_optimization.py index 5e1fd55b0377..7527858eb55e 100644 --- a/genetic_algorithm/function_optimization.py +++ b/genetic_algorithm/function_optimization.py @@ -5,7 +5,7 @@ """ import random -from typing import Callable, Tuple, List +from collections.abc import Callable # Define the parameters for the genetic algorithm N_POPULATION = 100 @@ -14,7 +14,7 @@ NUM_GENERATIONS = 50 -def evaluate(func: Callable, params: List[float]) -> float: +def evaluate(func: Callable, params: list[float]) -> float: """ Evaluate the fitness of an individual based on the provided function. @@ -27,14 +27,14 @@ def evaluate(func: Callable, params: List[float]) -> float: return func(*params) -def crossover(parent_1: List[float], parent_2: List[float]) -> List[float]: +def crossover(parent_1: list[float], parent_2: list[float]) -> list[float]: """Perform crossover between two parents.""" crossover_point = random.randint(1, len(parent_1) - 1) child = parent_1[:crossover_point] + parent_2[crossover_point:] return child -def mutate(individual: List[float], mutation_rate: float) -> List[float]: +def mutate(individual: list[float], mutation_rate: float) -> list[float]: """Mutate an individual with a certain probability.""" mutated_individual = [] for gene in individual: @@ -48,8 +48,8 @@ def mutate(individual: List[float], mutation_rate: float) -> List[float]: def select( - population: List[Tuple[List[float], float]], num_selected: int -) -> List[List[float]]: + population: list[tuple[list[float], float]], num_selected: int +) -> list[list[float]]: """Select individuals based on their fitness scores.""" sorted_population = sorted(population, key=lambda x: x[1]) selected_parents = [ @@ -60,10 +60,8 @@ def select( def optimize( func: Callable, - num_params: int, - param_ranges: List[Tuple[float, float]], - optimization_goal: str, -) -> Tuple[List[float], float]: + param_ranges: list[tuple[float, float]], +) -> tuple[list[float], float]: """Optimize the given function using a genetic algorithm.""" # Initialize the population population = [ @@ -72,7 +70,7 @@ def optimize( ] # Main optimization loop - for generation in range(NUM_GENERATIONS): + for _generation in range(NUM_GENERATIONS): # Evaluate the fitness of each individual in the population population_fitness = [ (individual, evaluate(func, individual)) for individual in population @@ -83,7 +81,7 @@ def optimize( # Generate offspring through crossover and mutation offspring = [] - for i in range(N_POPULATION): + for _i in range(N_POPULATION): parent_1 = random.choice(selected_parents) parent_2 = random.choice(selected_parents) child = crossover(parent_1, parent_2) @@ -109,8 +107,6 @@ def quadratic_function(x, y): return x**2 + y**2 param_ranges = [(-10, 10), (-10, 10)] - best_params, best_fitness = optimize( - quadratic_function, 2, param_ranges, "minimize" - ) + best_params, best_fitness = optimize(quadratic_function, param_ranges) print("Best parameters:", best_params) print("Best fitness:", best_fitness) From 6fc3bbdd4dee58a9d34a15b5f87c46c6a7be78b9 Mon Sep 17 00:00:00 2001 From: KelvinPuyam Date: Sat, 20 Apr 2024 01:31:43 +0530 Subject: [PATCH 4/4] Used Sequence in optimize --- genetic_algorithm/function_optimization.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/genetic_algorithm/function_optimization.py b/genetic_algorithm/function_optimization.py index 7527858eb55e..f7342ff80bc6 100644 --- a/genetic_algorithm/function_optimization.py +++ b/genetic_algorithm/function_optimization.py @@ -6,6 +6,7 @@ import random from collections.abc import Callable +from typing import Sequence # Define the parameters for the genetic algorithm N_POPULATION = 100 @@ -60,8 +61,8 @@ def select( def optimize( func: Callable, - param_ranges: list[tuple[float, float]], -) -> tuple[list[float], float]: + param_ranges: Sequence[tuple[float, float]], +) -> tuple[Sequence[float], float]: """Optimize the given function using a genetic algorithm.""" # Initialize the population population = [