@@ -9,11 +9,11 @@ def parse_function(user_input):
9
9
user_input (str): The user-defined fitness function in string format.
10
10
11
11
Returns:
12
- str: The function code as a string .
12
+ function: A callable fitness function .
13
13
14
14
Examples:
15
15
>>> parse_function("f(x, y) = x^2 + y^2")
16
- 'def fitness(x): \\ n return x[0]**2 + x[1]**2'
16
+ <function fitness at 0x...>
17
17
"""
18
18
user_input = user_input .strip ()
19
19
@@ -23,10 +23,14 @@ def parse_function(user_input):
23
23
else :
24
24
raise ValueError ("Invalid function format. Please use 'f(x, y) = ...'." )
25
25
26
- function_code = "def fitness(x): \n "
27
- function_code += f" return { expression .replace ('^' , '**' ).replace ('x' , 'x[0]' ).replace ('y' , 'x[1]' )} \n "
26
+ # Replace variable names and power operator
27
+ expression = expression .replace ('^' , '**' ).replace ('x' , 'x[0]' ).replace ('y' , 'x[1]' )
28
28
29
- return function_code
29
+ # Create the fitness function
30
+ def fitness (x ):
31
+ return eval (expression )
32
+
33
+ return fitness
30
34
31
35
32
36
def genetic_algorithm (user_fitness_function ) -> None :
@@ -52,6 +56,7 @@ def genetic_algorithm(user_fitness_function) -> None:
52
56
best_fitness = np .inf
53
57
best_solution = None
54
58
59
+ # Initialize the population
55
60
population = rng .random ((population_size , chromosome_length ))
56
61
57
62
for generation in range (num_generations ):
@@ -60,9 +65,10 @@ def genetic_algorithm(user_fitness_function) -> None:
60
65
for individual in population :
61
66
fitness_value = user_fitness_function (individual )
62
67
63
- if fitness_value is None :
68
+ if fitness_value is None or not isinstance ( fitness_value , ( int , float )) :
64
69
print (
65
- f"Warning: Fitness function returned None for individual { individual } ."
70
+ f"Warning: Fitness function returned an invalid value "
71
+ f"for individual { individual } ."
66
72
)
67
73
fitness_value = np .inf
68
74
else :
@@ -74,26 +80,35 @@ def genetic_algorithm(user_fitness_function) -> None:
74
80
75
81
fitness_values = np .array (fitness_values )
76
82
83
+ # Update the best solution
77
84
best_idx = np .argmin (fitness_values )
78
85
if fitness_values [best_idx ] < best_fitness :
79
86
best_fitness = fitness_values [best_idx ]
80
87
best_solution = population [best_idx ]
81
88
82
89
print (f"Generation { generation + 1 } , Best Fitness: { best_fitness :.6f} " )
83
90
91
+ # Selection
84
92
selected_parents = population [rng .choice (population_size , population_size )]
85
-
93
+
94
+ # Crossover
86
95
offspring = []
87
- for i in range (0 , population_size , 2 ):
96
+ for i in range (0 , population_size - 1 , 2 ): # Ensure even number of parents
88
97
parent1 , parent2 = selected_parents [i ], selected_parents [i + 1 ]
89
98
cross_point = rng .integers (1 , chromosome_length )
90
99
child1 = np .concatenate ((parent1 [:cross_point ], parent2 [cross_point :]))
91
100
child2 = np .concatenate ((parent2 [:cross_point ], parent1 [cross_point :]))
92
101
offspring .append (child1 )
93
102
offspring .append (child2 )
94
103
104
+ # Handle odd population size if necessary
105
+ if population_size % 2 == 1 :
106
+ offspring .append (selected_parents [- 1 ]) # Include last parent if odd
107
+
95
108
offspring = np .array (offspring )
96
- mutation_mask = rng .random ((population_size , chromosome_length )) < mutation_rate
109
+
110
+ # Mutation
111
+ mutation_mask = rng .random (offspring .shape ) < mutation_rate
97
112
offspring [mutation_mask ] = rng .random (np .sum (mutation_mask ))
98
113
99
114
population = offspring
@@ -102,12 +117,14 @@ def genetic_algorithm(user_fitness_function) -> None:
102
117
print (f"User-defined function: f(x, y) = { user_input .split ('=' )[1 ].strip ()} " )
103
118
print (f"Best Fitness Value (Minimum): { best_fitness :.6f} " )
104
119
print (
105
- f"Optimal Solution Found: x = { best_solution [0 ]:.6f} , y = { best_solution [1 ]:.6f} "
120
+ f"Optimal Solution Found: x = { best_solution [0 ]:.6f} , "
121
+ f"y = { best_solution [1 ]:.6f} "
106
122
)
107
123
108
124
function_value = best_fitness
109
125
print (
110
- f"Function Value at Optimal Solution: f({ best_solution [0 ]:.6f} , { best_solution [1 ]:.6f} ) = { function_value :.6f} "
126
+ f"Function Value at Optimal Solution: f({ best_solution [0 ]:.6f} , "
127
+ f"{ best_solution [1 ]:.6f} ) = { function_value :.6f} "
111
128
)
112
129
113
130
@@ -117,15 +134,9 @@ def genetic_algorithm(user_fitness_function) -> None:
117
134
)
118
135
119
136
try :
120
- fitness_function_code = parse_function (user_input )
121
- exec ( fitness_function_code , globals () )
137
+ fitness_function = parse_function (user_input )
138
+ genetic_algorithm ( fitness_function )
122
139
except (SyntaxError , ValueError ) as e :
123
140
print (f"Error: { e } " )
124
141
except NameError as e :
125
- print (f"Error: { e } " )
126
-
127
- if "fitness" in globals ():
128
- if "fitness" in globals ():
129
- genetic_algorithm (globals ()["fitness" ])
130
- else :
131
- print ("No valid fitness function provided." )
142
+ print (f"Error: { e } " )
0 commit comments