Project-Platypus / Platypus

A Free and Open Source Python Library for Multiobjective Optimization
GNU General Public License v3.0
563 stars 153 forks source link

Max number of functions evaluations doesn't behave as expected with InjectedPopulation #136

Closed Orbitography closed 3 years ago

Orbitography commented 4 years ago

Hello, The following code doesn't work as expected :

from platypus import (OMOPSO,
                      Problem,
                      Real,
                      InjectedPopulation)

def schaffer(x):
    return [x[0]**3, (x[0]-2)**3]

def callback_function(algorithm):
    print(algorithm.nfe)

problem = Problem(1, 2)
problem.types[:] = Real(-10, 10)
problem.function = schaffer

algorithm = OMOPSO(problem,
                   swarm_size=20,
                   epsilons=[0.05, 0.05])
algorithm.run(5000,
              callback = callback_function)

init_pop = algorithm.result

algorithm2 = OMOPSO(problem,
                    generator=InjectedPopulation(init_pop),
                    swarm_size=20,
                    epsilons=[0.05, 0.05])

algorithm2.run(5000,
               callback = callback_function)

I expected a result like :

20
40
60
....
5000

20
40
...
5000

but instead i got :

20
40
60
....
5000

19
39
59
...
5019

But it works as expected when i use the usual shaffer function :

def schaffer(x):
    return [x[0]**2, (x[0]-2)**2]

My problem is that my cost function takes a long time to compute and i can't afford to make more functions evaluations than expected.

joergfelder commented 4 years ago

That's interesting. I have some similar issues which I just described in #62 . I initialise with a population from a previous run and the algorithm run returns strange results - a lot of them outside the constraints. I have not monitored nfe though. Maybe that is something to look for. Let me know, if you find a solution.

dhadka commented 4 years ago

@Orbitography Essentially, the init_pop is already evaluated, so when you pass it into the new algorithm with InjectedPopulation, it does not count those against the total NFE since it does not need to re-evaluated them. The final NFE goes beyond your limit of 5000 because it runs a complete iteration of the algorithm, which for OMOPSO is equal to the swarm_size. So, for example, the second to last iteration had NFE=4999, which is less than 5000, so it ran another iteration increasing the NFE by 20 to 5019.

Given the numbers you see are 19, 39, 59, ..., I suspect init_pop only contains 1 solution. This is because algorithm.result only returns the non-dominated solutions. (You could look at using algorithm.particles or algorithm.leaders instead for PSO). You can print init_pop to confirm that is the case. Since the swarm_size is 20 but it's only injecting 1 solution, it fills in the remaining 19 with new, random solutions that count against the NFE.

One option if you really want it to stop at 5000 is to just "round up" the NFE at the beginning: algorithm2.nfe = algorithm2.swarm_size.

dhadka commented 4 years ago

@joergfelder If you are injecting feasible solutions but then find algorithm.result is returning infeasible solutions, that's something I would want to investigate as it shouldn't usually happen. Do you have a code example I can run that reproduces the issue?

joergfelder commented 4 years ago

@dhadka Thanks for your reply. I will condense my code down so that I can send it to you for evaluation. It is similar to what I have just added in #62 - but I will compile it into something that you can work with. I hope later tonight is ok for you ...

joergfelder commented 4 years ago

Dear David,

First of all my thanks for providing platypus and also for looking into the issue I currently have.

I copied the two files you need to observe the effect. The CSV file contains solutions from previous optimisation runs. The python file is longly - basically because I left all the variables and range checking in it so that it is compatible with the cdv file. The functions “CoilQualityMeasures” is my function to be optimised. In the demo code I send you, it returns fixed objective values. The function “ReadResultFile” reads the cdv file and returns non dominated solutions.

All the platypus fun happens from line 329 to 353.

And I added some plotting so that you can see that the optimisation is only called one or twice when restarted and the solutions contain a lot of uninitialised values that seem to violate constraints as well …

I hope you can find some info that helps me to restart my optimisation loop from the values I have stored already …

Thanks in advance Jörg

On 1. Apr 2020, at 16:21, David Hadka notifications@github.com wrote:

@joergfelder https://github.com/joergfelder If you are injecting feasible solutions but then find algorithm.result is returning infeasible solutions, that's something I would want to investigate as it shouldn't usually happen. Do you have a code example I can run that reproduces the issue?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/Project-Platypus/Platypus/issues/136#issuecomment-607278608, or unsubscribe https://github.com/notifications/unsubscribe-auth/ALVYMRCZSVFMTULOKZN53ADRKNEU5ANCNFSM4LXN3QOA.

joergfelder commented 4 years ago

@dhadka I am not sure if the append files come through. Could you let me know if you received them ...

I did some more tests and I am not sure if my problem arises from the fact that I initialise the population with the solutions from previous runs and then reduce the number of algorithm executions. Would it be more consistent to set algorithm.nfe to the number of prior executions?

Orbitography commented 3 years ago

Thank you for your comprehensive answer Mr. Hadka. It helped me solve my problem. I apologize for the time I took to close this issue.