Skip to content

Commit 5ff30a6

Browse files
committed
Update genetic_algorithm_optimization.py
1 parent 40e4e3c commit 5ff30a6

File tree

1 file changed

+14
-75
lines changed

1 file changed

+14
-75
lines changed

genetic_algorithm/genetic_algorithm_optimization.py

+14-75
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1+
from collections.abc import Callable # Fixes the UP035 warning
12
import numpy as np
2-
from typing import Callable, List, Tuple
3-
43

54
class GeneticAlgorithmOptimizer:
65
def __init__(
76
self,
8-
objective_function: Callable[..., float],
9-
variable_bounds: List[Tuple[float, float]],
7+
objective_function: Callable[..., float],
8+
variable_bounds: list[tuple[float, float]],
109
population_size: int = 100,
1110
max_generations: int = 500,
1211
crossover_probability: float = 0.9,
@@ -26,33 +25,16 @@ def __init__(
2625
def generate_initial_population(self) -> np.ndarray:
2726
"""
2827
Generate a population of random solutions within the given variable bounds.
29-
30-
>>> optimizer = GeneticAlgorithmOptimizer(
31-
... objective_function=lambda x: x**2,
32-
... variable_bounds=[(-10, 10)]
33-
... )
34-
>>> population = optimizer.generate_initial_population()
35-
>>> population.shape == (optimizer.population_size, optimizer.num_variables)
36-
True
3728
"""
3829
return self.rng.uniform(
3930
low=self.variable_bounds[:, 0],
4031
high=self.variable_bounds[:, 1],
4132
size=(self.population_size, self.num_variables),
4233
)
4334

44-
def evaluate_fitness(self, individual: List[float]) -> float:
35+
def evaluate_fitness(self, individual: list[float]) -> float:
4536
"""
4637
Evaluate the fitness of an individual by computing the value of the objective function.
47-
48-
>>> optimizer = GeneticAlgorithmOptimizer(
49-
... objective_function=lambda x: x**2,
50-
... variable_bounds=[(-10, 10)]
51-
... )
52-
>>> optimizer.evaluate_fitness([2])
53-
4
54-
>>> optimizer.evaluate_fitness([0])
55-
0
5638
"""
5739
return self.objective_function(*individual)
5840

@@ -61,16 +43,6 @@ def select_parent(
6143
) -> np.ndarray:
6244
"""
6345
Select a parent using tournament selection based on fitness values.
64-
65-
>>> optimizer = GeneticAlgorithmOptimizer(
66-
... objective_function=lambda x: x**2,
67-
... variable_bounds=[(-10, 10)]
68-
... )
69-
>>> population = optimizer.generate_initial_population()
70-
>>> fitness_values = np.array([optimizer.evaluate_fitness(ind) for ind in population])
71-
>>> parent = optimizer.select_parent(population, fitness_values)
72-
>>> len(parent) == optimizer.num_variables
73-
True
7446
"""
7547
selected_indices = self.rng.choice(
7648
range(self.population_size), size=2, replace=False
@@ -79,57 +51,34 @@ def select_parent(
7951

8052
def perform_crossover(
8153
self, parent1: np.ndarray, parent2: np.ndarray
82-
) -> Tuple[np.ndarray, np.ndarray]:
54+
) -> tuple[np.ndarray, np.ndarray]:
8355
"""
8456
Perform one-point crossover between two parents to create offspring.
8557
Skip crossover for single-variable functions.
86-
87-
>>> optimizer = GeneticAlgorithmOptimizer(
88-
... objective_function=lambda x: x**2,
89-
... variable_bounds=[(-10, 10)]
90-
... )
91-
>>> parent1 = [1]
92-
>>> parent2 = [2]
93-
>>> child1, child2 = optimizer.perform_crossover(parent1, parent2)
94-
>>> child1 == parent1 and child2 == parent2
95-
True
9658
"""
9759
if self.num_variables == 1:
9860
return parent1, parent2
9961

10062
if self.rng.random() < self.crossover_probability:
10163
crossover_point = self.rng.integers(1, self.num_variables)
102-
child1 = np.concatenate(
103-
(parent1[:crossover_point], parent2[crossover_point:])
104-
)
105-
child2 = np.concatenate(
106-
(parent2[:crossover_point], parent1[crossover_point:])
107-
)
64+
child1 = np.concatenate((parent1[:crossover_point], parent2[crossover_point:]))
65+
child2 = np.concatenate((parent2[:crossover_point], parent1[crossover_point:]))
10866
return child1, child2
10967
return parent1, parent2
11068

11169
def apply_mutation(self, individual: np.ndarray) -> np.ndarray:
11270
"""
11371
Apply mutation to an individual based on the mutation probability.
114-
115-
>>> optimizer = GeneticAlgorithmOptimizer(
116-
... objective_function=lambda x: x**2,
117-
... variable_bounds=[(-10, 10)]
118-
... )
119-
>>> individual = [1]
120-
>>> mutated_individual = optimizer.apply_mutation(individual.copy())
121-
>>> len(mutated_individual) == len(individual)
122-
True
12372
"""
12473
if self.rng.random() < self.mutation_probability:
12574
mutation_index = self.rng.integers(0, self.num_variables)
12675
individual[mutation_index] = self.rng.uniform(
127-
self.variable_bounds[mutation_index, 0],
128-
self.variable_bounds[mutation_index, 1],
76+
self.variable_bounds[mutation_index, 0],
77+
self.variable_bounds[mutation_index, 1]
12978
)
13079
return individual
13180

132-
def optimize(self) -> Tuple[np.ndarray, float]:
81+
def optimize(self) -> tuple[np.ndarray, float]:
13382
"""
13483
Execute the genetic algorithm over a number of generations to find the optimal solution.
13584
"""
@@ -159,32 +108,22 @@ def optimize(self) -> Tuple[np.ndarray, float]:
159108
best_fitness_value = fitness_values[min_fitness_index]
160109
best_solution = population[min_fitness_index]
161110

162-
print(
163-
f"Generation {generation + 1}, Best Fitness Value: {best_fitness_value}"
164-
)
111+
print(f"Generation {generation + 1}, Best Fitness Value: {best_fitness_value}")
165112

166113
return best_solution, best_fitness_value
167114

168-
169115
if __name__ == "__main__":
170-
171116
def objective_function(x: float, y: float) -> float:
172117
"""
173118
Example objective function to minimize x^2 + y^2
174-
175-
>>> objective_function(0, 0)
176-
0
177-
>>> objective_function(3, 4)
178-
25
179-
>>> objective_function(-3, -4)
180-
25
181119
"""
182120
return x**2 + y**2
183121

184-
variable_bounds: List[Tuple[float, float]] = [(-10, 10), (-10, 10)]
122+
variable_bounds: list[tuple[float, float]] = [(-10, 10), (-10, 10)]
185123

186124
optimizer = GeneticAlgorithmOptimizer(
187-
objective_function=objective_function, variable_bounds=variable_bounds
125+
objective_function=objective_function,
126+
variable_bounds=variable_bounds
188127
)
189128
best_solution, best_fitness_value = optimizer.optimize()
190129

0 commit comments

Comments
 (0)