@@ -25,7 +25,7 @@ def solve_duplicate_genes_randomly(self,
25
25
max_val (int): The maximum value of the range to sample a number randomly.
26
26
mutation_by_replacement (bool): Indicates if mutation is performed by replacement.
27
27
gene_type (type): The data type of the gene (e.g., int, float).
28
- num_trials (int): The maximum number of attempts to resolve duplicates by changing the gene values.
28
+ num_trials (int): The maximum number of attempts to resolve duplicates by changing the gene values. Only works for floating-point gene types.
29
29
30
30
Returns:
31
31
tuple:
@@ -42,53 +42,48 @@ def solve_duplicate_genes_randomly(self,
42
42
num_unsolved_duplicates = 0
43
43
if len (not_unique_indices ) > 0 :
44
44
for duplicate_index in not_unique_indices :
45
- for trial_index in range (num_trials ):
46
- if self .gene_type_single == True :
47
- dtype = gene_type
48
- else :
49
- dtype = gene_type [duplicate_index ]
50
-
51
- if dtype [0 ] in pygad .GA .supported_int_types :
52
- temp_val = self .unique_int_gene_from_range (solution = new_solution ,
53
- gene_index = duplicate_index ,
54
- min_val = min_val ,
55
- max_val = max_val ,
56
- mutation_by_replacement = mutation_by_replacement ,
57
- gene_type = gene_type )
58
- else :
59
- temp_val = numpy .random .uniform (low = min_val ,
60
- high = max_val ,
61
- size = 1 )[0 ]
62
- if mutation_by_replacement :
45
+ if self .gene_type_single == True :
46
+ dtype = gene_type
47
+ else :
48
+ dtype = gene_type [duplicate_index ]
49
+
50
+ if dtype [0 ] in pygad .GA .supported_int_types :
51
+ temp_val = self .unique_int_gene_from_range (solution = new_solution ,
52
+ gene_index = duplicate_index ,
53
+ min_val = min_val ,
54
+ max_val = max_val ,
55
+ mutation_by_replacement = mutation_by_replacement ,
56
+ gene_type = gene_type )
57
+ else :
58
+ temp_val = self .unique_float_gene_from_range (solution = new_solution ,
59
+ gene_index = duplicate_index ,
60
+ min_val = min_val ,
61
+ max_val = max_val ,
62
+ mutation_by_replacement = mutation_by_replacement ,
63
+ gene_type = gene_type ,
64
+ num_trials = num_trials )
65
+ """
66
+ temp_val = numpy.random.uniform(low=min_val,
67
+ high=max_val,
68
+ size=1)[0]
69
+ if mutation_by_replacement:
63
70
pass
64
- else :
71
+ else:
65
72
temp_val = new_solution[duplicate_index] + temp_val
73
+ """
74
+
75
+ if temp_val in new_solution :
76
+ num_unsolved_duplicates = num_unsolved_duplicates + 1
77
+ if not self .suppress_warnings : warnings .warn (f"Failed to find a unique value for gene with index { duplicate_index } whose value is { solution [duplicate_index ]} . Consider adding more values in the gene space or use a wider range for initial population or random mutation." )
78
+ else :
79
+ # Unique gene value found.
80
+ new_solution [duplicate_index ] = temp_val
81
+
82
+ # Update the list of duplicate indices after each iteration.
83
+ _ , unique_gene_indices = numpy .unique (new_solution , return_index = True )
84
+ not_unique_indices = set (range (len (solution ))) - set (unique_gene_indices )
85
+ # self.logger.info("not_unique_indices INSIDE", not_unique_indices)
66
86
67
- # Similar to the round_genes() method in the pygad module,
68
- # Create a round_gene() method to round a single gene.
69
- if not dtype [1 ] is None :
70
- temp_val = numpy .round (dtype [0 ](temp_val ),
71
- dtype [1 ])
72
- else :
73
- temp_val = dtype [0 ](temp_val )
74
-
75
- if temp_val in new_solution and trial_index == (num_trials - 1 ):
76
- num_unsolved_duplicates = num_unsolved_duplicates + 1
77
- if not self .suppress_warnings : warnings .warn (f"Failed to find a unique value for gene with index { duplicate_index } whose value is { solution [duplicate_index ]} . Consider adding more values in the gene space or use a wider range for initial population or random mutation." )
78
- elif temp_val in new_solution :
79
- # Keep trying in the other remaining trials.
80
- continue
81
- else :
82
- # Unique gene value found.
83
- new_solution [duplicate_index ] = temp_val
84
- break
85
-
86
- # TODO Move this code outside the loops.
87
- # Update the list of duplicate indices after each iteration.
88
- _ , unique_gene_indices = numpy .unique (new_solution , return_index = True )
89
- not_unique_indices = set (range (len (solution ))) - set (unique_gene_indices )
90
- # self.logger.info("not_unique_indices INSIDE", not_unique_indices)
91
-
92
87
return new_solution , not_unique_indices , num_unsolved_duplicates
93
88
94
89
def solve_duplicate_genes_by_space (self ,
@@ -167,14 +162,14 @@ def unique_int_gene_from_range(self,
167
162
Args:
168
163
solution (list): A solution containing genes, potentially with duplicate values.
169
164
gene_index (int): The index of the gene for which to find a unique value.
170
- min_val (int): The minimum value of the range to sample a number randomly.
171
- max_val (int): The maximum value of the range to sample a number randomly.
165
+ min_val (int): The minimum value of the range to sample an integer randomly.
166
+ max_val (int): The maximum value of the range to sample an integer randomly.
172
167
mutation_by_replacement (bool): Indicates if mutation is performed by replacement.
173
- gene_type (type): The data type of the gene (e.g., int, float ).
168
+ gene_type (type): The data type of the gene (e.g., int, int8, uint16, etc ).
174
169
step (int, optional): The step size for generating candidate values. Defaults to 1.
175
170
176
171
Returns:
177
- int: The new value of the gene. If no unique value can be found, the original gene value is returned.
172
+ int: The new integer value of the gene. If no unique value can be found, the original gene value is returned.
178
173
"""
179
174
180
175
# The gene_type is of the form [type, precision]
@@ -194,22 +189,86 @@ def unique_int_gene_from_range(self,
194
189
else :
195
190
all_gene_values = all_gene_values + solution [gene_index ]
196
191
197
- # After adding solution[gene_index] to the list, we have to change the data type again.
198
- # TODO: The gene data type is converted twine. One above and one here.
199
- all_gene_values = numpy .asarray (all_gene_values ,
200
- dtype )
192
+ # After adding solution[gene_index] to the list, we have to change the data type again.
193
+ all_gene_values = numpy .asarray (all_gene_values ,
194
+ dtype )
201
195
202
196
values_to_select_from = list (set (list (all_gene_values )) - set (solution ))
203
197
204
198
if len (values_to_select_from ) == 0 :
205
199
# If there are no values, then keep the current gene value.
206
- if not self .suppress_warnings : warnings .warn ("You set 'allow_duplicate_genes=False' but there is no enough values to prevent duplicates." )
207
200
selected_value = solution [gene_index ]
208
201
else :
209
202
selected_value = random .choice (values_to_select_from )
203
+
204
+ selected_value = dtype [0 ](selected_value )
210
205
211
206
return selected_value
212
207
208
+ def unique_float_gene_from_range (self ,
209
+ solution ,
210
+ gene_index ,
211
+ min_val ,
212
+ max_val ,
213
+ mutation_by_replacement ,
214
+ gene_type ,
215
+ num_trials = 10 ):
216
+
217
+ """
218
+ Finds a unique floating-point value for a specific gene in a solution.
219
+
220
+ Args:
221
+ solution (list): A solution containing genes, potentially with duplicate values.
222
+ gene_index (int): The index of the gene for which to find a unique value.
223
+ min_val (int): The minimum value of the range to sample a floating-point number randomly.
224
+ max_val (int): The maximum value of the range to sample a floating-point number randomly.
225
+ mutation_by_replacement (bool): Indicates if mutation is performed by replacement.
226
+ gene_type (type): The data type of the gene (e.g., float, float16, float32, etc).
227
+ num_trials (int): The maximum number of attempts to resolve duplicates by changing the gene values.
228
+
229
+ Returns:
230
+ int: The new floating-point value of the gene. If no unique value can be found, the original gene value is returned.
231
+ """
232
+
233
+ # The gene_type is of the form [type, precision]
234
+ dtype = gene_type
235
+
236
+ for trial_index in range (num_trials ):
237
+ temp_val = numpy .random .uniform (low = min_val ,
238
+ high = max_val ,
239
+ size = 1 )[0 ]
240
+
241
+ # If mutation is by replacement, do not add the current gene value into the list.
242
+ # This is to avoid replacing the value by itself again. We are doing nothing in this case.
243
+ if mutation_by_replacement :
244
+ pass
245
+ else :
246
+ temp_val = temp_val + solution [gene_index ]
247
+
248
+ if not dtype [1 ] is None :
249
+ # Precision is available and we have to round the number.
250
+ # Convert the data type and round the number.
251
+ temp_val = numpy .round (dtype [0 ](temp_val ),
252
+ dtype [1 ])
253
+ else :
254
+ # There is no precision and rounding the number is not needed. The type is [type, None]
255
+ # Just convert the data type.
256
+ temp_val = dtype [0 ](temp_val )
257
+
258
+ if temp_val in solution and trial_index == (num_trials - 1 ):
259
+ # If there are no values, then keep the current gene value.
260
+ if not self .suppress_warnings : warnings .warn ("You set 'allow_duplicate_genes=False' but cannot find a value to prevent duplicates." )
261
+ selected_value = solution [gene_index ]
262
+ elif temp_val in solution :
263
+ # Keep trying in the other remaining trials.
264
+ continue
265
+ else :
266
+ # Unique gene value found.
267
+ selected_value = temp_val
268
+ break
269
+
270
+ return selected_value
271
+
213
272
def unique_genes_by_space (self ,
214
273
new_solution ,
215
274
gene_type ,
@@ -225,7 +284,7 @@ def unique_genes_by_space(self,
225
284
new_solution (list): A solution containing genes with duplicate values.
226
285
gene_type (type): The data type of the gene (e.g., int, float).
227
286
not_unique_indices (list): The indices of genes with duplicate values.
228
- num_trials (int): The maximum number of attempts to resolve duplicates for each gene.
287
+ num_trials (int): The maximum number of attempts to resolve duplicates for each gene. Only works for floating-point numbers.
229
288
230
289
Returns:
231
290
tuple:
@@ -236,22 +295,18 @@ def unique_genes_by_space(self,
236
295
237
296
num_unsolved_duplicates = 0
238
297
for duplicate_index in not_unique_indices :
239
- for trial_index in range (num_trials ):
240
- temp_val = self .unique_gene_by_space (solution = new_solution ,
241
- gene_idx = duplicate_index ,
242
- gene_type = gene_type ,
243
- build_initial_pop = build_initial_pop )
244
-
245
- if temp_val in new_solution and trial_index == (num_trials - 1 ):
246
- # self.logger.info("temp_val, duplicate_index", temp_val, duplicate_index, new_solution)
247
- num_unsolved_duplicates = num_unsolved_duplicates + 1
248
- if not self .suppress_warnings : warnings .warn (f"Failed to find a unique value for gene with index { duplicate_index } whose value is { new_solution [duplicate_index ]} . Consider adding more values in the gene space or use a wider range for initial population or random mutation." )
249
- elif temp_val in new_solution :
250
- continue
251
- else :
252
- new_solution [duplicate_index ] = temp_val
253
- # self.logger.info("SOLVED", duplicate_index)
254
- break
298
+ temp_val = self .unique_gene_by_space (solution = new_solution ,
299
+ gene_idx = duplicate_index ,
300
+ gene_type = gene_type ,
301
+ build_initial_pop = build_initial_pop ,
302
+ num_trials = num_trials )
303
+
304
+ if temp_val in new_solution :
305
+ # self.logger.info("temp_val, duplicate_index", temp_val, duplicate_index, new_solution)
306
+ num_unsolved_duplicates = num_unsolved_duplicates + 1
307
+ if not self .suppress_warnings : warnings .warn (f"Failed to find a unique value for gene with index { duplicate_index } whose value is { new_solution [duplicate_index ]} . Consider adding more values in the gene space or use a wider range for initial population or random mutation." )
308
+ else :
309
+ new_solution [duplicate_index ] = temp_val
255
310
256
311
# Update the list of duplicate indices after each iteration.
257
312
_ , unique_gene_indices = numpy .unique (new_solution , return_index = True )
@@ -264,7 +319,8 @@ def unique_gene_by_space(self,
264
319
solution ,
265
320
gene_idx ,
266
321
gene_type ,
267
- build_initial_pop = False ):
322
+ build_initial_pop = False ,
323
+ num_trials = 10 ):
268
324
269
325
"""
270
326
Returns a unique value for a specific gene based on its value space to resolve duplicates.
@@ -273,6 +329,7 @@ def unique_gene_by_space(self,
273
329
solution (list): A solution containing genes with duplicate values.
274
330
gene_idx (int): The index of the gene that has a duplicate value.
275
331
gene_type (type): The data type of the gene (e.g., int, float).
332
+ num_trials (int): The maximum number of attempts to resolve duplicates for each gene. Only works for floating-point numbers.
276
333
277
334
Returns:
278
335
Any: A unique value for the gene, if one exists; otherwise, the original gene value. """
@@ -320,9 +377,20 @@ def unique_gene_by_space(self,
320
377
low = self .random_mutation_min_val
321
378
high = self .random_mutation_max_val
322
379
380
+ """
323
381
value_from_space = numpy.random.uniform(low=low,
324
382
high=high,
325
383
size=1)[0]
384
+ """
385
+
386
+ value_from_space = self .unique_float_gene_from_range (solution = solution ,
387
+ gene_index = gene_idx ,
388
+ min_val = low ,
389
+ max_val = high ,
390
+ mutation_by_replacement = True ,
391
+ gene_type = dtype ,
392
+ num_trials = num_trials )
393
+
326
394
327
395
elif type (curr_gene_space ) is dict :
328
396
if self .gene_type_single == True :
0 commit comments