Skip to content

Commit

Permalink
Support of NSGA-II
Browse files Browse the repository at this point in the history
  • Loading branch information
ahmedfgad committed Sep 3, 2023
1 parent 0ea3674 commit 4ef8cd5
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 6 deletions.
24 changes: 24 additions & 0 deletions pygad/pygad.py
Original file line number Diff line number Diff line change
Expand Up @@ -1683,6 +1683,12 @@ def cal_pop_fitness(self):
if self.fitness_batch_size in [1, None]:
fitness = self.fitness_func(self, sol, sol_idx)
if type(fitness) in GA.supported_int_float_types:
# The fitness function returns a single numeric value.
# This is a single-objective optimization problem.
pass
elif type(fitness) in [list, tuple, numpy.ndarray]:
# The fitness function returns a list/tuple/numpy.ndarray.
# This is a multi-objective optimization problem.
pass
else:
raise ValueError(f"The fitness function should return a number but the value {fitness} of type {type(fitness)} found.")
Expand Down Expand Up @@ -1718,6 +1724,12 @@ def cal_pop_fitness(self):

for index, fitness in zip(batch_indices, batch_fitness):
if type(fitness) in GA.supported_int_float_types:
# The fitness function returns a single numeric value.
# This is a single-objective optimization problem.
pop_fitness[index] = fitness
elif type(fitness) in [list, tuple, numpy.ndarray]:
# The fitness function returns a list/tuple/numpy.ndarray.
# This is a multi-objective optimization problem.
pop_fitness[index] = fitness
else:
raise ValueError(f"The fitness function should return a number but the value {fitness} of type {type(fitness)} found.")
Expand Down Expand Up @@ -1779,6 +1791,12 @@ def cal_pop_fitness(self):
if self.fitness_batch_size in [1, None]:
for index, fitness in zip(solutions_to_submit_indices, executor.map(self.fitness_func, [self]*len(solutions_to_submit_indices), solutions_to_submit, solutions_to_submit_indices)):
if type(fitness) in GA.supported_int_float_types:
# The fitness function returns a single numeric value.
# This is a single-objective optimization problem.
pop_fitness[index] = fitness
elif type(fitness) in [list, tuple, numpy.ndarray]:
# The fitness function returns a list/tuple/numpy.ndarray.
# This is a multi-objective optimization problem.
pop_fitness[index] = fitness
else:
raise ValueError(f"The fitness function should return a number but the value {fitness} of type {type(fitness)} found.")
Expand Down Expand Up @@ -1810,6 +1828,12 @@ def cal_pop_fitness(self):

for index, fitness in zip(batch_indices, batch_fitness):
if type(fitness) in GA.supported_int_float_types:
# The fitness function returns a single numeric value.
# This is a single-objective optimization problem.
pop_fitness[index] = fitness
elif type(fitness) in [list, tuple, numpy.ndarray]:
# The fitness function returns a list/tuple/numpy.ndarray.
# This is a multi-objective optimization problem.
pop_fitness[index] = fitness
else:
raise ValueError(f"The fitness function should return a number but the value ({fitness}) of type {type(fitness)} found.")
Expand Down
10 changes: 8 additions & 2 deletions pygad/utils/mutation.py
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,13 @@ def adaptive_mutation_population_fitness(self, offspring):

first_idx = len(parents_to_keep)
last_idx = fitness.shape[0]
fitness[first_idx:last_idx] = [0]*(last_idx - first_idx)
if len(fitness.shape) > 1:
# TODO This is a multi-objective optimization problem.
# fitness[first_idx:last_idx] = [0]*(last_idx - first_idx)
raise ValueError('Edit adaptive mutation to work with multi-objective optimization problems.')
else:
# This is a single-objective optimization problem.
fitness[first_idx:last_idx] = [0]*(last_idx - first_idx)

if self.fitness_batch_size in [1, None]:
# Calculate the fitness for each individual solution.
Expand Down Expand Up @@ -667,7 +673,7 @@ def adaptive_mutation_by_space(self, offspring):
gene_type=self.gene_type,
num_trials=10)
return offspring

def adaptive_mutation_randomly(self, offspring):

"""
Expand Down
14 changes: 10 additions & 4 deletions pygad/utils/parent_selection.py
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,9 @@ def nsga2_selection(self,

# The number of remaining parents to be selected.
num_remaining_parents = num_parents


# Index of the current parent.
current_parent_idx = 0
# A loop variable holding the index of the current pareto front.
pareto_front_idx = 0
while num_remaining_parents != 0 and pareto_front_idx < len(pareto_fronts):
Expand All @@ -407,10 +409,12 @@ def nsga2_selection(self,
for sol_idx in range(len(current_pareto_front)):
selected_solution_idx = current_pareto_front[sol_idx, 0]
# Insert the parent into the parents array.
parents[sol_idx, :] = self.population[selected_solution_idx, :].copy()
parents[current_parent_idx, :] = self.population[selected_solution_idx, :].copy()
# Insert the index of the selected parent.
parents_indices.append(selected_solution_idx)

# Increase the parent index.
current_parent_idx += 1

# Decrement the number of remaining parents by the length of the pareto front.
num_remaining_parents -= len(current_pareto_front)
else:
Expand All @@ -422,9 +426,11 @@ def nsga2_selection(self,

for selected_solution_idx in crowding_dist_pop_sorted_indices[0:num_remaining_parents]:
# Insert the parent into the parents array.
parents[sol_idx, :] = self.population[selected_solution_idx, :].copy()
parents[current_parent_idx, :] = self.population[selected_solution_idx, :].copy()
# Insert the index of the selected parent.
parents_indices.append(selected_solution_idx)
# Increase the parent index.
current_parent_idx += 1

# Decrement the number of remaining parents by the number of selected parents.
num_remaining_parents -= num_remaining_parents
Expand Down

0 comments on commit 4ef8cd5

Please sign in to comment.