anyoptimization / pymoo

NSGA2, NSGA3, R-NSGA3, MOEAD, Genetic Algorithms (GA), Differential Evolution (DE), CMAES, PSO
https://pymoo.org
Apache License 2.0
2.26k stars 389 forks source link

RVEA not reaching specified number of evaluations #564

Open jordan-cork opened 8 months ago

jordan-cork commented 8 months ago

RVEA does not complete the specified number of evaluations set by the termination condition.

I've run RVEA on several problems, both those implemented in pymoo and those implemented elsewhere, and never is the algorithm able to achieve the set number of evaluations. Mostly, it finishes in max_evals - pop_size evaluations, however, often it is less, sometimes significantly so. I'm assuming this is a bug and not a feature, as I cannot find mention of some early stopping mechanism in the paper.

As an example of this, I ran RVEA with 100 reference points (Riesz s-energy method) on a large set of 2 and 3 objective problems. The minimize function was used and the termination criteria was 10000 evaluations. Below are the results from individual runs on the CTP problems:

Problem | Total Evaluations
CTP1 | 8246
CTP2 | 9061
CTP3 | 9172
CTP4 | 9101
CTP5 | 9052
CTP6 | 9684
CTP7 | 9631
CTP8 | 9734

One part of the issue is likely due to the setup method that converts the evaluations termination to a generations termination. Here, pop_size is negated from the maximum number of evaluations. In the example above, this results in maximum evaluations being set to 99 as opposed to 100.

    def _setup(self, problem, **kwargs):

        # if maximum functions termination convert it to generations
        if isinstance(self.termination, MaximumFunctionCallTermination):
            n_gen = np.ceil((self.termination.n_max_evals - self.pop_size) / self.n_offsprings)
            self.termination = MaximumGenerationTermination(n_gen)

        # check whether the n_gen termination is used - otherwise this algorithm can be not run
        if not isinstance(self.termination, MaximumGenerationTermination):
            raise Exception("Please use the n_gen or n_eval as a termination criterion to run RVEA!")

The other part may actually be a feature of the survival mechanism. The algorithm's evaluator is recording the correct number of evaluations, despite there being less solutions in the population.

blankjul commented 8 months ago

RVEA was proposed as a unconstrained algorithm, thus I don't think it makes sense to test it on the CTP problems.

Originally, the paper has only proposed to have number of generations as termination criterion. However, we have added this fix to provide number of function evaluations as well.

# if maximum functions termination convert it to generations
if isinstance(self.termination, MaximumFunctionCallTermination):
    n_gen = np.ceil((self.termination.n_max_evals - self.pop_size) / self.n_offsprings)
    self.termination = MaximumGenerationTermination(n_gen)

# check whether the n_gen termination is used - otherwise this algorithm can be not run
if not isinstance(self.termination, MaximumGenerationTermination):
    raise Exception("Please use the n_gen or n_eval as a termination criterion to run RVEA!")
blankjul commented 8 months ago

Have you noticed the algorithm terminating early by fixing the number of generations as well?

jordan-cork commented 8 months ago

When I fix the number of generations, it completes these correctly and the algorithm's evaluator records the correct number of evaluations. However, when I 'save_history' and check for the number of solutions saved, there are less. The same occurs when I record solutions using a callback.

I'm now testing on the WFG, DTLZ and ZDT suites and I get the same outcomes.

blankjul commented 8 months ago

I am not sure if I can follow.

However, when I 'save_history' and check for the number of solutions saved, there are less. 

Not all evaluations are stored in the populatio. It might be an offspring is not improving the current population so it will not be contributing. Or are you actually trying to count all offsprings in each iteration?

jordan-cork commented 8 months ago

I meant that the res.history object does not store all evaluated solutions. It was my understanding it should.

Also, just to note, RVEA is listed as handling constraints on this page. Perhaps, it shouldn't?

blankjul commented 7 months ago

Thanks for calling this out! I have updated the table. See my commit above.