ljvmiranda921 / pyswarms

A research toolkit for particle swarm optimization in Python
https://pyswarms.readthedocs.io/en/latest/
MIT License
1.29k stars 333 forks source link

The cost function is called twice for an iteration? #257

Closed guven6 closed 5 years ago

guven6 commented 6 years ago

Is your feature request related to a problem? Please describe. A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

Describe the solution you'd like A clear and concise description of what you want to happen.

Describe alternatives you've considered A clear and concise description of any alternative solutions or features you've considered.

Additional context Add any other context or screenshots about the feature request here.

guven6 commented 6 years ago

We have a cost function which is very costly to compute - more than an hour. In the cost function I log the iterations (positions and current cost) and it is called twice for a single iteration (specified in optimizer.optimize). Is this expected? Any way to improve it?

ljvmiranda921 commented 6 years ago

Hi @guven6 ,

We will deal with algo optimizations on the next release (v.0.5.0), it might still take a long time since we’re dealing with other tasks

One workaround for your problem is to define your own optimization loop using pyswarms.backend. You can inherit the Optimizer you are currently using then override the optimize() method.

pdiaz2 commented 6 years ago

I have just come across to the same problem. The main issue is that you execute the objective function twice because you recalculate the particle best cost. I am not sure why this is (perhaps reading from memory is slower) but for long optimizations (such as myself and @jazcap53) this is surely not useful.

This is the part of the code in global_best.py that is responsible for this:

self.swarm.current_cost = objective_func(self.swarm.position, **kwargs) self.swarm.pbest_cost = objective_func(self.swarm.pbest_pos, **kwargs)

The solution is initialization of the particles best position to a very large number. I have not yet implemented this but would like advice on how to do it.

ljvmiranda921 commented 6 years ago

Hi @guven6 and @pdiaz2

Sorry I still feel a bit stuck on how to solve this, would you mind expounding what

initialization of the particles best position to a very large number`

mean? Thank you for reporting this issue and hopefully we can work on it right away.

cc: @whzup @SioKCronin mind helping me out? :+1: What's a good way to remove the duplicate call on our objective function?

pdiaz2 commented 6 years ago

First of all, I am somewhat new to github so I don't know how to properly cite lines in files etc., so you will have to excuse me :relaxed: I will just paste the lines then:

        for i in range(iters):
            # Compute cost for current position and personal best
            self.swarm.current_cost = objective_func(self.swarm.position, **kwargs)
            self.swarm.pbest_cost = objective_func(self.swarm.pbest_pos, **kwargs)
            self.swarm.pbest_pos, self.swarm.pbest_cost = compute_pbest(
                self.swarm
            )

The loop continues but as you can see the objective function is called twice: once to calculate for the entire swarm and once again to recalculate each particle's best cost. Since this algorithm is iterative, the particle's current best cost was calculated in the past iteration. For iteration 0, I see you initialize the each particle's best cost to an empty array. This is done on the swarms.py module. Changing this to a large number should do the trick and thus the second call can be eliminated.

Please correct me if I am saying anything that is not accurate, but I believe that this is the way Matlab PSO does it (as a matter of fact, I am translating a previous library I wrote in Matlab)

ljvmiranda921 commented 6 years ago

Hi @pdiaz2 !

No it's definitely fine! And thank you for walking me through the solution. Forgive me if I will ask a lot of clarifications so that I'm sure that I understand the proposed solution well. So from what I understood:

Hope I got the idea well. If this is OK, we'll try to make a fix end of month (most of the maintainers have day jobs or in university, so it's difficult to dedicate full-time here, unfortunately, inasmuch as we want to). But if you are interested in making a Pull Request, it would really help us and we'd really appreciate your help!

pdiaz2 commented 6 years ago

Not exactly. Let me try to explain:

The main thing is that at iteration t you should already have each particle's best cost stored in swarm.pbest_cost. If you are only storing the position but not the cost then you need to call the objective function twice. Looking at your code it seems that each particle's best cost is saved through all iterations in swarm.pbest_cost so when you call the objective function again (on each best position) it should return the same cost that is already stored.

Regarding pulling, since I am new to github I would rather not do so. Besides, I am working currently on something else so I too am running low on time. Should I overwrite your method? The simplest way is to assign numpy.inf to the cost outside the loop.

ljvmiranda921 commented 6 years ago

Okay, now it's much clearer!

Should I overwrite your method? The simplest way is to assign numpy.inf to the cost outside the loop.

Yes, you can inherit GlobalBestPSO and override it. If it solves the problem, then please tell us right away so that we can incorporate here in the project.

Thank you @pdiaz2 !

pdiaz2 commented 6 years ago

Perfect! I will work on this and return as soon as possible. Thank you!

pdiaz2 commented 6 years ago

Hello, I have solved this issue. I am actually running an industrial application with in real time and through the use of apscheduler (python cron-like scheduling package) I also discovered what I think is a major flaw in your algorithm design.

The particles positions are generated when the optimizer is instantiated (GlobalBestPSO for example). This means that when running repeated iterations of the PSO (like I am doing with apscheduler) but not executing the script again the particles always start up in the exact same position. The swarm should be generated when the optimizer is called, at the beginning of the PSO algorithm and outside of the iterative loop.

Please correct me if I am wrong. As I said, regarding the first issue I have already corrected it.

ljvmiranda921 commented 6 years ago

Hi @pdiaz2 ,

The particles starting up in the same position is by design. The idea is that if you want to rerun PSO again with different starting positions, then you should call the reset() method.

import pyswarms as ps
optimizer = ps.single.GlobalBestPSO(*args, **kwargs)
optimizer.optimize()

# If you wish to run again with different starting positions
optimizer.reset() # this will re-generate the positions again
pdiaz2 commented 6 years ago

Oh, thanks! Maybe I missed the example! Thanks, I will be sharing the code as soon as possible!

ljvmiranda921 commented 6 years ago

Glad to hear that!

stale[bot] commented 5 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

guven6 commented 5 years ago

@pdiaz2 , Do you have solution? Would you like to share it? Thanks in advance.

pdiaz2 commented 5 years ago

Yes I do. Since I am not very good at github I think the best way is to share upload the modified optimizer function directly. I will do this on Friday if I get the chance, Christmas at the most.

pdiaz2 commented 5 years ago

Hi! Here is a file that contains the modified optimizer. I added a lot of enhancements to it, but the one discussed in this post is specially emphasized. I also modified your compute_position method because it masked the entire particle instead of just the dimension that ended up out of bounds. Since I was only looking for something functional, I did not overwrite that method in the topology library but instead generated one for the optimizer. I leave said method as well.

pyswarms_corrected.txt

danielcorreia96 commented 5 years ago

Hi @ljvmiranda921 and @pdiaz2 ,

I've opened a pull request based on the solution file provided in the previous comment (full credits to @pdiaz2 🏆 ), which removes the second cost function call using the pbest_cost initialization to np.inf.

ljvmiranda921 commented 5 years ago

Hi @danielcorreia96 ! Welcome to Pyswarms! That's really awesome :+1: Ok, we'll review your work in a while