1
1
import numpy as np
2
2
from typing import Callable , List , Tuple
3
3
4
-
5
4
class GeneticAlgorithmOptimizer :
6
5
def __init__ (
7
6
self ,
8
- objective_function : Callable [..., float ], # The function that returns the value to be minimized
9
- variable_bounds : List [Tuple [float , float ]], # List of tuples specifying the bounds for each variable
7
+ objective_function : Callable [..., float ],
8
+ variable_bounds : List [Tuple [float , float ]],
10
9
population_size : int = 100 ,
11
10
max_generations : int = 500 ,
12
11
crossover_probability : float = 0.9 ,
@@ -27,7 +26,10 @@ def generate_initial_population(self) -> np.ndarray:
27
26
"""
28
27
Generate a population of random solutions within the given variable bounds.
29
28
30
- >>> optimizer = GeneticAlgorithmOptimizer(objective_function=lambda x: x**2, variable_bounds=[(-10, 10)])
29
+ >>> optimizer = GeneticAlgorithmOptimizer(
30
+ ... objective_function=lambda x: x**2,
31
+ ... variable_bounds=[(-10, 10)]
32
+ ... )
31
33
>>> population = optimizer.generate_initial_population()
32
34
>>> population.shape == (optimizer.population_size, optimizer.num_variables)
33
35
True
@@ -42,19 +44,27 @@ def evaluate_fitness(self, individual: List[float]) -> float:
42
44
"""
43
45
Evaluate the fitness of an individual by computing the value of the objective function.
44
46
45
- >>> optimizer = GeneticAlgorithmOptimizer(objective_function=lambda x: x**2, variable_bounds=[(-10, 10)])
47
+ >>> optimizer = GeneticAlgorithmOptimizer(
48
+ ... objective_function=lambda x: x**2,
49
+ ... variable_bounds=[(-10, 10)]
50
+ ... )
46
51
>>> optimizer.evaluate_fitness([2])
47
52
4
48
53
>>> optimizer.evaluate_fitness([0])
49
54
0
50
55
"""
51
56
return self .objective_function (* individual )
52
57
53
- def select_parent (self , population : np .ndarray , fitness_values : np .ndarray ) -> np .ndarray :
58
+ def select_parent (
59
+ self , population : np .ndarray , fitness_values : np .ndarray
60
+ ) -> np .ndarray :
54
61
"""
55
62
Select a parent using tournament selection based on fitness values.
56
63
57
- >>> optimizer = GeneticAlgorithmOptimizer(objective_function=lambda x: x**2, variable_bounds=[(-10, 10)])
64
+ >>> optimizer = GeneticAlgorithmOptimizer(
65
+ ... objective_function=lambda x: x**2,
66
+ ... variable_bounds=[(-10, 10)]
67
+ ... )
58
68
>>> population = optimizer.generate_initial_population()
59
69
>>> fitness_values = np.array([optimizer.evaluate_fitness(ind) for ind in population])
60
70
>>> parent = optimizer.select_parent(population, fitness_values)
@@ -66,19 +76,25 @@ def select_parent(self, population: np.ndarray, fitness_values: np.ndarray) -> n
66
76
)
67
77
return population [selected_indices [np .argmin (fitness_values [selected_indices ])]]
68
78
69
- def perform_crossover (self , parent1 : np .ndarray , parent2 : np .ndarray ) -> Tuple [np .ndarray , np .ndarray ]:
79
+ def perform_crossover (
80
+ self , parent1 : np .ndarray , parent2 : np .ndarray
81
+ ) -> Tuple [np .ndarray , np .ndarray ]:
70
82
"""
71
- Perform one-point crossover between two parents to create offspring. Skip crossover for single-variable functions.
83
+ Perform one-point crossover between two parents to create offspring.
84
+ Skip crossover for single-variable functions.
72
85
73
- >>> optimizer = GeneticAlgorithmOptimizer(objective_function=lambda x: x**2, variable_bounds=[(-10, 10)])
86
+ >>> optimizer = GeneticAlgorithmOptimizer(
87
+ ... objective_function=lambda x: x**2,
88
+ ... variable_bounds=[(-10, 10)]
89
+ ... )
74
90
>>> parent1 = [1]
75
91
>>> parent2 = [2]
76
92
>>> child1, child2 = optimizer.perform_crossover(parent1, parent2)
77
93
>>> child1 == parent1 and child2 == parent2
78
94
True
79
95
"""
80
96
if self .num_variables == 1 :
81
- return parent1 , parent2 # No crossover needed for single-variable functions
97
+ return parent1 , parent2
82
98
83
99
if self .rng .random () < self .crossover_probability :
84
100
crossover_point = self .rng .integers (1 , self .num_variables )
@@ -91,7 +107,10 @@ def apply_mutation(self, individual: np.ndarray) -> np.ndarray:
91
107
"""
92
108
Apply mutation to an individual based on the mutation probability.
93
109
94
- >>> optimizer = GeneticAlgorithmOptimizer(objective_function=lambda x: x**2, variable_bounds=[(-10, 10)])
110
+ >>> optimizer = GeneticAlgorithmOptimizer(
111
+ ... objective_function=lambda x: x**2,
112
+ ... variable_bounds=[(-10, 10)]
113
+ ... )
95
114
>>> individual = [1]
96
115
>>> mutated_individual = optimizer.apply_mutation(individual.copy())
97
116
>>> len(mutated_individual) == len(individual)
@@ -100,7 +119,8 @@ def apply_mutation(self, individual: np.ndarray) -> np.ndarray:
100
119
if self .rng .random () < self .mutation_probability :
101
120
mutation_index = self .rng .integers (0 , self .num_variables )
102
121
individual [mutation_index ] = self .rng .uniform (
103
- self .variable_bounds [mutation_index , 0 ], self .variable_bounds [mutation_index , 1 ]
122
+ self .variable_bounds [mutation_index , 0 ],
123
+ self .variable_bounds [mutation_index , 1 ]
104
124
)
105
125
return individual
106
126
@@ -138,9 +158,7 @@ def optimize(self) -> Tuple[np.ndarray, float]:
138
158
139
159
return best_solution , best_fitness_value
140
160
141
-
142
161
if __name__ == "__main__" :
143
- # Define the function to optimize
144
162
def objective_function (x : float , y : float ) -> float :
145
163
"""
146
164
Example objective function to minimize x^2 + y^2
@@ -152,13 +170,14 @@ def objective_function(x: float, y: float) -> float:
152
170
>>> objective_function(-3, -4)
153
171
25
154
172
"""
155
- return x ** 2 + y ** 2 # Example: Minimizing x^2 + y^2
173
+ return x ** 2 + y ** 2
156
174
157
- # Define the bounds for each variable
158
175
variable_bounds : List [Tuple [float , float ]] = [(- 10 , 10 ), (- 10 , 10 )]
159
176
160
- # Initialize and run the optimizer
161
- optimizer = GeneticAlgorithmOptimizer (objective_function = objective_function , variable_bounds = variable_bounds )
177
+ optimizer = GeneticAlgorithmOptimizer (
178
+ objective_function = objective_function ,
179
+ variable_bounds = variable_bounds
180
+ )
162
181
best_solution , best_fitness_value = optimizer .optimize ()
163
182
164
183
print ("Best Solution:" , best_solution )
0 commit comments