jMetal / jMetalPy

A framework for single/multi-objective optimization with metaheuristics
https://jmetal.github.io/jMetalPy/index.html
MIT License
498 stars 150 forks source link

get_non_domiated_solutions() seems not return expected pf solutions with 3 objectives #85

Closed easyfly007 closed 1 year ago

easyfly007 commented 3 years ago

Hello, thanks for the great tool of jmetalpy and i use it in my optimization problems and it works quite well. the only issue I observed is that when I run it for 3 objectives optimization, the function get_non_domiated_solutions() seems didn't return the required PF result. it always return 1 PF point no matter how many points the original solutions has. to find out if this is my usage issue or potentially code risk, I try to look into the jmetalpy code and find that in: file util/archive.py:

     def add(self, solution: S) -> bool:
        is_dominated = False
        is_contained = False

        if len(self.solution_list) == 0:
            self.solution_list.append(solution)
            return True
        else:
            number_of_deleted_solutions = 0

            # New copy of list and enumerate
            for index, current_solution in enumerate(list(self.solution_list)):
                is_dominated_flag = self.comparator.compare(solution, current_solution)
                if is_dominated_flag == -1:
                    del self.solution_list[index - number_of_deleted_solutions]
                    number_of_deleted_solutions += 1
                elif is_dominated_flag == 1:
                    is_dominated = True
                    break
                elif is_dominated_flag == 0:
                    if solution.objectives == current_solution.objectives:
                        is_contained = True
                        break

        if not is_dominated and not is_contained:
            self.solution_list.append(solution)
            return True

        return False

I am afriad that when del some member in the original list when looping it, it may deleted the unexpeced member there. I think when during the looping of enumerate(), it will always get updated the list and then we are iterate the unexpected members

I did some simple test and find the issue:

num_deleted = 0
a = [10,11,12, 13, 14]
for i,v in enumerate(a):
    print("")
    print('i =', i, ', v = ', v)
    print('a = ',  a)

    if v % 2 == 0:
        print("condition is true, v {} deleted".format(str(a[i- num_deleted ])))
        del a[i - num_deleted]
    num_deleted += 1

print('finally, a = ', a)

what I want is to get the final a = [11, 13], but actually I got is [13, 14]

ajnebro commented 3 years ago

Hi @easyfly007. I'm running the nsgaiii_dtlz2.py program after adding the get_non_dominated_solutions() function in this way:


    algorithm.run()
    front = get_non_dominated_solutions(algorithm.get_result())

The front have 92 solutions, which is the expected result.

How are you using that function, in order to allow us to reproduce the situation where only a solution is returned?

easyfly007 commented 3 years ago

thanks anjebro, I will try to reproduce the issue in my optimizations and will post how to reproduce it when I have one.