From 7e068ddd25edb18ded0011d4a070d870644678ec Mon Sep 17 00:00:00 2001 From: Ruslan Shaiakhmetov <42977731+Crylab@users.noreply.github.com> Date: Tue, 8 Jul 2025 01:42:10 +0200 Subject: [PATCH 1/2] fix: best_solution for multi-objective optimization This fix allows to optimize both single-objective and multi-objective optimization cases --- pygad/pygad.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/pygad/pygad.py b/pygad/pygad.py index 3661dab..d0413ad 100644 --- a/pygad/pygad.py +++ b/pygad/pygad.py @@ -2227,8 +2227,19 @@ def best_solution(self, pop_fitness=None): raise ValueError(f"The type of the 'pop_fitness' parameter is expected to be list, tuple, or numpy.ndarray but ({type(pop_fitness)}) found.") # Return the index of the best solution that has the best fitness value. - best_match_idx = numpy.where( - pop_fitness == numpy.max(pop_fitness))[0][0] + # For multi-objective optimization: find the index of the solution with the maximum fitness in the first objective, + # break ties using the second objective, then third, etc. + pop_fitness_arr = numpy.array(pop_fitness) + # Get the indices that would sort by all objectives in descending order + sorted_indices = numpy.lexsort([ -pop_fitness_arr[:,i] for i in reversed(range(pop_fitness_arr.shape[1])) ]) + best_match_idx = sorted_indices[0] + maximum_fitness_value = pop_fitness_arr[best_match_idx] + + best_match_list = numpy.where( + pop_fitness == maximum_fitness_value) + + best_match_idx = best_match_list[0][0] # Get the first index of the best match. + best_solution = self.population[best_match_idx, :].copy() best_solution_fitness = pop_fitness[best_match_idx] @@ -2494,4 +2505,4 @@ def load(filename): except: # raise BaseException("Error loading the file. If the file already exists, please reload all the functions previously used (e.g. fitness function).") raise BaseException("Error loading the file.") - return ga_in \ No newline at end of file + return ga_in From b2ac512b9de5f6516c664e38f13ee41e80d6bdf2 Mon Sep 17 00:00:00 2001 From: Ruslan Shaiakhmetov <42977731+Crylab@users.noreply.github.com> Date: Tue, 8 Jul 2025 12:22:20 +0200 Subject: [PATCH 2/2] fix: keep original code + fix for multi-objective optimization --- pygad/pygad.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/pygad/pygad.py b/pygad/pygad.py index d0413ad..66e4b31 100644 --- a/pygad/pygad.py +++ b/pygad/pygad.py @@ -2231,14 +2231,22 @@ def best_solution(self, pop_fitness=None): # break ties using the second objective, then third, etc. pop_fitness_arr = numpy.array(pop_fitness) # Get the indices that would sort by all objectives in descending order - sorted_indices = numpy.lexsort([ -pop_fitness_arr[:,i] for i in reversed(range(pop_fitness_arr.shape[1])) ]) - best_match_idx = sorted_indices[0] - maximum_fitness_value = pop_fitness_arr[best_match_idx] - - best_match_list = numpy.where( - pop_fitness == maximum_fitness_value) + if pop_fitness_arr.ndim == 1: + # Single-objective optimization. + best_match_idx = numpy.where( + pop_fitness == numpy.max(pop_fitness))[0][0] + elif pop_fitness_arr.ndim == 2: + # Multi-objective optimization. + # Sort by all objectives in descending order. + # The first objective is the most important, then the second, etc. + sorted_indices = numpy.lexsort([ -pop_fitness_arr[:,i] for i in reversed(range(pop_fitness_arr.shape[1])) ]) + best_match_idx = sorted_indices[0] + maximum_fitness_value = pop_fitness_arr[best_match_idx] + + best_match_list = numpy.where( + pop_fitness == maximum_fitness_value) - best_match_idx = best_match_list[0][0] # Get the first index of the best match. + best_match_idx = best_match_list[0][0] # Get the first index of the best match. best_solution = self.population[best_match_idx, :].copy()