1
1
import numpy
2
2
3
- population_fitness = numpy .array ([[20 , 2.2 ],
4
- [60 , 4.4 ],
5
- [65 , 3.5 ],
6
- [15 , 4.4 ],
7
- [55 , 4.5 ],
8
- [50 , 1.8 ],
9
- [80 , 4.0 ],
10
- [25 , 4.6 ]])
3
+ fitness = numpy .array ([[20 , 2.2 ],
4
+ [60 , 4.4 ],
5
+ [65 , 3.5 ],
6
+ [15 , 4.4 ],
7
+ [55 , 4.5 ],
8
+ [50 , 1.8 ],
9
+ [80 , 4.0 ],
10
+ [25 , 4.6 ]])
11
+
12
+ # fitness = numpy.array([20,
13
+ # 60,
14
+ # 65,
15
+ # 15,
16
+ # 55,
17
+ # 50,
18
+ # 80,
19
+ # 25])
20
+
21
+ # fitness = numpy.array([[20],
22
+ # [60],
23
+ # [65],
24
+ # [15],
25
+ # [55],
26
+ # [50],
27
+ # [80],
28
+ # [25]])
11
29
12
30
def get_non_dominated_set (curr_solutions ):
13
31
"""
@@ -66,13 +84,13 @@ def get_non_dominated_set(curr_solutions):
66
84
# Return the dominated and non-dominated sets.
67
85
return dominated_set , non_dominated_set
68
86
69
- def non_dominated_sorting (population_fitness ):
87
+ def non_dominated_sorting (fitness ):
70
88
"""
71
- Apply the non-dominant sorting over the population_fitness to create the pareto fronts based on non-dominaned sorting of the solutions.
89
+ Apply the non-dominant sorting over the fitness to create the pareto fronts based on non-dominaned sorting of the solutions.
72
90
73
91
Parameters
74
92
----------
75
- population_fitness : TYPE
93
+ fitness : TYPE
76
94
An array of the population fitness across all objective function.
77
95
78
96
Returns
@@ -87,15 +105,15 @@ def non_dominated_sorting(population_fitness):
87
105
# The remaining set to be explored for non-dominance.
88
106
# Initially it is set to the entire population.
89
107
# The solutions of each non-dominated set are removed after each iteration.
90
- remaining_set = population_fitness .copy ()
108
+ remaining_set = fitness .copy ()
91
109
92
110
# Zipping the solution index with the solution's fitness.
93
111
# This helps to easily identify the index of each solution.
94
112
# Each element has:
95
113
# 1) The index of the solution.
96
114
# 2) An array of the fitness values of this solution across all objectives.
97
- # remaining_set = numpy.array(list(zip(range(0, population_fitness .shape[0]), non_dominated_set)))
98
- remaining_set = list (zip (range (0 , population_fitness .shape [0 ]), remaining_set ))
115
+ # remaining_set = numpy.array(list(zip(range(0, fitness .shape[0]), non_dominated_set)))
116
+ remaining_set = list (zip (range (0 , fitness .shape [0 ]), remaining_set ))
99
117
100
118
# A list mapping the index of each pareto front to the set of solutions in this front.
101
119
solutions_fronts_indices = [- 1 ]* len (remaining_set )
@@ -116,14 +134,16 @@ def non_dominated_sorting(population_fitness):
116
134
117
135
return pareto_fronts , solutions_fronts_indices
118
136
119
- def crowding_distance (pareto_front ):
137
+ def crowding_distance (pareto_front , fitness ):
120
138
"""
121
139
Calculate the crowding dstance for all solutions in the current pareto front.
122
140
123
141
Parameters
124
142
----------
125
143
pareto_front : TYPE
126
144
The set of solutions in the current pareto front.
145
+ fitness : TYPE
146
+ The fitness of the current population.
127
147
128
148
Returns
129
149
-------
@@ -164,8 +184,8 @@ def crowding_distance(pareto_front):
164
184
obj_sorted = sorted (obj , key = lambda x : x [1 ])
165
185
166
186
# Get the minimum and maximum values for the current objective.
167
- obj_min_val = min (population_fitness [:, obj_idx ])
168
- obj_max_val = max (population_fitness [:, obj_idx ])
187
+ obj_min_val = min (fitness [:, obj_idx ])
188
+ obj_max_val = max (fitness [:, obj_idx ])
169
189
denominator = obj_max_val - obj_min_val
170
190
# To avoid division by zero, set the denominator to a tiny value.
171
191
if denominator == 0 :
@@ -217,9 +237,11 @@ def crowding_distance(pareto_front):
217
237
return obj_crowding_dist_list , crowding_dist_sum , crowding_dist_front_sorted_indices , crowding_dist_pop_sorted_indices
218
238
219
239
def tournament_selection_nsga2 (self ,
220
- pareto_fronts ,
221
- solutions_fronts_indices ,
222
- num_parents ):
240
+ fitness ,
241
+ num_parents
242
+ # pareto_fronts,
243
+ # solutions_fronts_indices,
244
+ ):
223
245
224
246
"""
225
247
Select the parents using the tournament selection technique for NSGA-II.
@@ -231,9 +253,10 @@ def tournament_selection_nsga2(self,
231
253
Later, the selected parents will mate to produce the offspring.
232
254
233
255
It accepts 2 parameters:
256
+ -fitness: The fitness values for the current population.
257
+ -num_parents: The number of parents to be selected.
234
258
-pareto_fronts: A nested array of all the pareto fronts. Each front has its solutions.
235
259
-solutions_fronts_indices: A list of the pareto front index of each solution in the current population.
236
- -num_parents: The number of parents to be selected.
237
260
238
261
It returns an array of the selected parents alongside their indices in the population.
239
262
"""
@@ -246,6 +269,11 @@ def tournament_selection_nsga2(self,
246
269
# The indices of the selected parents.
247
270
parents_indices = []
248
271
272
+ # TODO If there is only a single objective, each pareto front is expected to have only 1 solution.
273
+ # TODO Make a test to check for that behaviour.
274
+ # Find the pareto fronts and the solutions' indicies in each front.
275
+ pareto_fronts , solutions_fronts_indices = non_dominated_sorting (fitness )
276
+
249
277
# Randomly generate pairs of indices to apply for NSGA-II tournament selection for selecting the parents solutions.
250
278
rand_indices = numpy .random .randint (low = 0.0 ,
251
279
high = len (solutions_fronts_indices ),
@@ -284,7 +312,8 @@ def tournament_selection_nsga2(self,
284
312
# Reaching here means the pareto front has more than 1 solution.
285
313
286
314
# Calculate the crowding distance of the solutions of the pareto front.
287
- obj_crowding_distance_list , crowding_distance_sum , crowding_dist_front_sorted_indices , crowding_dist_pop_sorted_indices = crowding_distance (pareto_front .copy ())
315
+ obj_crowding_distance_list , crowding_distance_sum , crowding_dist_front_sorted_indices , crowding_dist_pop_sorted_indices = crowding_distance (pareto_front = pareto_front .copy (),
316
+ fitness = fitness )
288
317
289
318
# This list has the sorted front-based indices for the solutions in the current pareto front.
290
319
crowding_dist_front_sorted_indices = list (crowding_dist_front_sorted_indices )
@@ -333,9 +362,11 @@ def tournament_selection_nsga2(self,
333
362
return parents , numpy .array (parents_indices )
334
363
335
364
def nsga2_selection (self ,
336
- pareto_fronts ,
337
- solutions_fronts_indices ,
338
- num_parents ):
365
+ fitness ,
366
+ num_parents
367
+ # pareto_fronts,
368
+ # solutions_fronts_indices
369
+ ):
339
370
340
371
"""
341
372
Select the parents using the Non-Dominated Sorting Genetic Algorithm II (NSGA-II).
@@ -348,9 +379,10 @@ def nsga2_selection(self,
348
379
Later, the selected parents will mate to produce the offspring.
349
380
350
381
It accepts 2 parameters:
382
+ -fitness: The fitness values for the current population.
383
+ -num_parents: The number of parents to be selected.
351
384
-pareto_fronts: A nested array of all the pareto fronts. Each front has its solutions.
352
385
-solutions_fronts_indices: A list of the pareto front index of each solution in the current population.
353
- -num_parents: The number of parents to be selected.
354
386
355
387
It returns an array of the selected parents alongside their indices in the population.
356
388
"""
@@ -363,6 +395,11 @@ def nsga2_selection(self,
363
395
# The indices of the selected parents.
364
396
parents_indices = []
365
397
398
+ # TODO If there is only a single objective, each pareto front is expected to have only 1 solution.
399
+ # TODO Make a test to check for that behaviour.
400
+ # Find the pareto fronts and the solutions' indicies in each front.
401
+ pareto_fronts , solutions_fronts_indices = non_dominated_sorting (fitness )
402
+
366
403
# The number of remaining parents to be selected.
367
404
num_remaining_parents = num_parents
368
405
@@ -387,7 +424,8 @@ def nsga2_selection(self,
387
424
# If only a subset of the front is needed, then use the crowding distance to sort the solutions and select only the number needed.
388
425
389
426
# Calculate the crowding distance of the solutions of the pareto front.
390
- obj_crowding_distance_list , crowding_distance_sum , crowding_dist_front_sorted_indices , crowding_dist_pop_sorted_indices = crowding_distance (current_pareto_front .copy ())
427
+ obj_crowding_distance_list , crowding_distance_sum , crowding_dist_front_sorted_indices , crowding_dist_pop_sorted_indices = crowding_distance (pareto_front = current_pareto_front .copy (),
428
+ fitness = fitness )
391
429
392
430
for selected_solution_idx in crowding_dist_pop_sorted_indices [0 :num_remaining_parents ]:
393
431
# Insert the parent into the parents array.
@@ -404,8 +442,10 @@ def nsga2_selection(self,
404
442
# Make sure the parents indices is returned as a NumPy array.
405
443
return parents , numpy .array (parents_indices )
406
444
407
-
408
- pareto_fronts , solutions_fronts_indices = non_dominated_sorting (population_fitness )
445
+ # TODO If there is only a single objective, each pareto front is expected to have only 1 solution.
446
+ # TODO Make a test to check for that behaviour.
447
+ # Find the pareto fronts and the solutions' indicies in each front.
448
+ pareto_fronts , solutions_fronts_indices = non_dominated_sorting (fitness )
409
449
# # print('\nsolutions_fronts_indices\n', solutions_fronts_indices)
410
450
# for i, s in enumerate(pareto_fronts):
411
451
# # print(f'Dominated Pareto Front Set {i+1}:\n{s}')
@@ -422,27 +462,33 @@ class Object(object):
422
462
obj .K_tournament = 2
423
463
424
464
parents , parents_indices = tournament_selection_nsga2 (self = obj ,
425
- pareto_fronts = pareto_fronts ,
426
- solutions_fronts_indices = solutions_fronts_indices ,
427
- num_parents = 40 )
465
+ fitness = fitness ,
466
+ num_parents = 4
467
+ # pareto_fronts=pareto_fronts,
468
+ # solutions_fronts_indices=solutions_fronts_indices,
469
+ )
428
470
print (f'Tournament Parent Selection for NSGA-II - Indices: \n { parents_indices } ' )
429
471
430
472
parents , parents_indices = nsga2_selection (self = obj ,
431
- pareto_fronts = pareto_fronts ,
432
- solutions_fronts_indices = solutions_fronts_indices ,
433
- num_parents = 40 )
473
+ fitness = fitness ,
474
+ num_parents = 4
475
+ # pareto_fronts=pareto_fronts,
476
+ # solutions_fronts_indices=solutions_fronts_indices,
477
+ )
434
478
print (f'NSGA-II Parent Selection - Indices: \n { parents_indices } ' )
435
479
436
480
# for idx in range(len(pareto_fronts)):
437
481
# # Fetch the current pareto front.
438
482
# pareto_front = pareto_fronts[idx]
439
- # obj_crowding_distance_list, crowding_distance_sum, crowding_dist_front_sorted_indices, crowding_dist_pop_sorted_indices = crowding_distance(pareto_front.copy())
483
+ # obj_crowding_distance_list, crowding_distance_sum, crowding_dist_front_sorted_indices, crowding_dist_pop_sorted_indices = crowding_distance(pareto_front=pareto_front.copy(),
484
+ # fitness=fitness)
440
485
# print('Front IDX', crowding_dist_front_sorted_indices)
441
486
# print('POP IDX ', crowding_dist_pop_sorted_indices)
442
487
# print(f'Sorted Sum of Crowd Dists\n{crowding_distance_sum}')
443
488
444
489
# # Fetch the current pareto front.
445
490
# pareto_front = pareto_fronts[0]
446
- # obj_crowding_distance_list, crowding_distance_sum, crowding_dist_front_sorted_indices, crowding_dist_pop_sorted_indices = crowding_distance(pareto_front.copy())
491
+ # obj_crowding_distance_list, crowding_distance_sum, crowding_dist_front_sorted_indices, crowding_dist_pop_sorted_indices = crowding_distance(pareto_front=pareto_front.copy(),
492
+ # fitness=fitness)
447
493
# print('\n', crowding_dist_pop_sorted_indices)
448
494
# print(f'Sorted Sum of Crowd Dists\n{crowding_distance_sum}')
0 commit comments