ljvmiranda921 / pyswarms

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

Extra documentation for custom objective function #484

Closed jmmcd closed 2 years ago

jmmcd commented 3 years ago

I found it surprising that the objective function has to accept the entire population (not a single particle) and return one value per particle. My students found it surprising too. In most treatments of optimisation, we define an objective function as a function that accepts a single solution and returns a single real number.

I see that it's documented at https://pyswarms.readthedocs.io/en/latest/api/pyswarms.utils.functions.html#module-pyswarms.utils.functions.single_obj:

"All objective functions obj_func() must accept a (numpy.ndarray) with shape (n_particles, dimensions). Thus, each row represents a particle, and each column represents its position on a specific dimension of the search-space.

In this context, obj_func() must return an array j of size (n_particles, ) that contains all the computed fitness for each particle."

Ok, I understand that in PySwarms, we are using the vectorised version for speed. I don't disagree with this design decision! Instead, a small addition to the docs would help.

In all the tutorials https://pyswarms.readthedocs.io/en/latest/tutorials.html, the objective is one of the built-in ones, and so the new user is not confronted with the issue. They try to write their own objective function which accepts a single particle, and they don't see any error -- but the algorithm is now not correct! This code runs but only calculates a single objective value for the entire population. I guess that with Numpy broadcasting, the algorithm now operates as if all particles have the same objective value.

import numpy as np
import pyswarms as ps
def my_sphere(x):
    return np.sum(x**2)

options = {'c1': 0.5, 'c2': 0.3, 'w':0.9}
optimizer = ps.single.GlobalBestPSO(n_particles=10, dimensions=2, options=options)
cost, pos = optimizer.optimize(my_sphere, iters=1000)

In other cases, depending on the definition of the objective, the user may see a confusing Numpy error message.

Solutions

  1. I would suggest to write a short section "Optimizing your own function" where the objective is hand-written, before the "Optimizing a function with bounds" tutorial section. It could mention this issue and basically use the above example, but show how to use axis=1 to fix the problem. I could do this and create a PR, what do you think?

  2. Could PySwarms make a specific check that the return value from the objective has the expected shape, ie n_particles, and write out a user-friendly error message if not? I guess this would be in https://github.com/jmmcd/pyswarms/blob/master/pyswarms/backend/operators.py#L213.

jmmcd commented 3 years ago

I implemented these changes here: https://github.com/jmmcd/pyswarms/

Unfortunately I won't have time to setup tests, CI etc to make an official PR for the code (item 2). For the docs (item 1) I can if needed.

ljvmiranda921 commented 3 years ago

Hi @jmmcd , thanks a lot for putting this together!

the objective is one of the built-in ones, and so the new user is not confronted with the issue. They try to write their own objective function which accepts a single particle, and they don't see any error -- but the algorithm is now not correct!

You're definitely right, the new user is not confronted with the issue when built-in ones are used. I also agree that the problem occurs one we start writing our own objective function. We do have a decorator for that, I guess that should be properly documented as well

Thanks for catching! I'll check with your fork and do the appropriate changes! There's a lot of things that I need to handle yet (including 3.6+ support, etc.). If you're interested to make a PR I'll appreciate that as well! 🙇

jmmcd commented 3 years ago

Hi @ljvmiranda921, I will just create the PR containing both the docs and the extra check. I won't have time to do tests etc so I will leave it in your hands :) Hope it helps.

stale[bot] commented 2 years ago

Is this still relevant? If so, what is blocking it? Is there anything you can do to help move it forward?

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