1
1
import numpy as np
2
2
from typing import Callable , List , Tuple
3
3
4
+
4
5
class GeneticAlgorithmOptimizer :
5
6
def __init__ (
6
7
self ,
7
- objective_function : Callable [..., float ],
8
- variable_bounds : List [Tuple [float , float ]],
8
+ objective_function : Callable [..., float ],
9
+ variable_bounds : List [Tuple [float , float ]],
9
10
population_size : int = 100 ,
10
11
max_generations : int = 500 ,
11
12
crossover_probability : float = 0.9 ,
@@ -27,7 +28,7 @@ def generate_initial_population(self) -> np.ndarray:
27
28
Generate a population of random solutions within the given variable bounds.
28
29
29
30
>>> optimizer = GeneticAlgorithmOptimizer(
30
- ... objective_function=lambda x: x**2,
31
+ ... objective_function=lambda x: x**2,
31
32
... variable_bounds=[(-10, 10)]
32
33
... )
33
34
>>> population = optimizer.generate_initial_population()
@@ -45,7 +46,7 @@ def evaluate_fitness(self, individual: List[float]) -> float:
45
46
Evaluate the fitness of an individual by computing the value of the objective function.
46
47
47
48
>>> optimizer = GeneticAlgorithmOptimizer(
48
- ... objective_function=lambda x: x**2,
49
+ ... objective_function=lambda x: x**2,
49
50
... variable_bounds=[(-10, 10)]
50
51
... )
51
52
>>> optimizer.evaluate_fitness([2])
@@ -62,7 +63,7 @@ def select_parent(
62
63
Select a parent using tournament selection based on fitness values.
63
64
64
65
>>> optimizer = GeneticAlgorithmOptimizer(
65
- ... objective_function=lambda x: x**2,
66
+ ... objective_function=lambda x: x**2,
66
67
... variable_bounds=[(-10, 10)]
67
68
... )
68
69
>>> population = optimizer.generate_initial_population()
@@ -80,11 +81,11 @@ def perform_crossover(
80
81
self , parent1 : np .ndarray , parent2 : np .ndarray
81
82
) -> Tuple [np .ndarray , np .ndarray ]:
82
83
"""
83
- Perform one-point crossover between two parents to create offspring.
84
+ Perform one-point crossover between two parents to create offspring.
84
85
Skip crossover for single-variable functions.
85
86
86
87
>>> optimizer = GeneticAlgorithmOptimizer(
87
- ... objective_function=lambda x: x**2,
88
+ ... objective_function=lambda x: x**2,
88
89
... variable_bounds=[(-10, 10)]
89
90
... )
90
91
>>> parent1 = [1]
@@ -98,8 +99,12 @@ def perform_crossover(
98
99
99
100
if self .rng .random () < self .crossover_probability :
100
101
crossover_point = self .rng .integers (1 , self .num_variables )
101
- child1 = np .concatenate ((parent1 [:crossover_point ], parent2 [crossover_point :]))
102
- child2 = np .concatenate ((parent2 [:crossover_point ], parent1 [crossover_point :]))
102
+ child1 = np .concatenate (
103
+ (parent1 [:crossover_point ], parent2 [crossover_point :])
104
+ )
105
+ child2 = np .concatenate (
106
+ (parent2 [:crossover_point ], parent1 [crossover_point :])
107
+ )
103
108
return child1 , child2
104
109
return parent1 , parent2
105
110
@@ -108,7 +113,7 @@ def apply_mutation(self, individual: np.ndarray) -> np.ndarray:
108
113
Apply mutation to an individual based on the mutation probability.
109
114
110
115
>>> optimizer = GeneticAlgorithmOptimizer(
111
- ... objective_function=lambda x: x**2,
116
+ ... objective_function=lambda x: x**2,
112
117
... variable_bounds=[(-10, 10)]
113
118
... )
114
119
>>> individual = [1]
@@ -119,8 +124,8 @@ def apply_mutation(self, individual: np.ndarray) -> np.ndarray:
119
124
if self .rng .random () < self .mutation_probability :
120
125
mutation_index = self .rng .integers (0 , self .num_variables )
121
126
individual [mutation_index ] = self .rng .uniform (
122
- self .variable_bounds [mutation_index , 0 ],
123
- self .variable_bounds [mutation_index , 1 ]
127
+ self .variable_bounds [mutation_index , 0 ],
128
+ self .variable_bounds [mutation_index , 1 ],
124
129
)
125
130
return individual
126
131
@@ -154,11 +159,15 @@ def optimize(self) -> Tuple[np.ndarray, float]:
154
159
best_fitness_value = fitness_values [min_fitness_index ]
155
160
best_solution = population [min_fitness_index ]
156
161
157
- print (f"Generation { generation + 1 } , Best Fitness Value: { best_fitness_value } " )
162
+ print (
163
+ f"Generation { generation + 1 } , Best Fitness Value: { best_fitness_value } "
164
+ )
158
165
159
166
return best_solution , best_fitness_value
160
167
168
+
161
169
if __name__ == "__main__" :
170
+
162
171
def objective_function (x : float , y : float ) -> float :
163
172
"""
164
173
Example objective function to minimize x^2 + y^2
@@ -175,8 +184,7 @@ def objective_function(x: float, y: float) -> float:
175
184
variable_bounds : List [Tuple [float , float ]] = [(- 10 , 10 ), (- 10 , 10 )]
176
185
177
186
optimizer = GeneticAlgorithmOptimizer (
178
- objective_function = objective_function ,
179
- variable_bounds = variable_bounds
187
+ objective_function = objective_function , variable_bounds = variable_bounds
180
188
)
181
189
best_solution , best_fitness_value = optimizer .optimize ()
182
190
0 commit comments