gugarosa / opytimizer

🐦 Opytimizer is a Python library consisting of meta-heuristic optimization algorithms.
https://opytimizer.readthedocs.io
Apache License 2.0
599 stars 40 forks source link

[BUG] Gaussian multiplicative noise may lead to unexpected behaviours on GA #21

Closed lzfelix closed 3 years ago

lzfelix commented 3 years ago

Describe the bug

Hello, I noticed that mutation in the GA algorithm is performed by multiplying a given agent coordinate by a value sampled from a Gaussian distribution with mean 0 and standard deviation 1 here. Under these configurations, it's very likely the sampled coefficient will be zero, or very close to it, and since x* (y ~ 0) -> 0 one of the agent's coordinate will be replaced by zero, which is also the optimal point for several benchmark functions.

Consequently, after multiple mutations, an agent may have several of its coordinates set to zero and the remainder of the algorithm will just fine-tune the position found by the Gaussian noise towards the function optima point. Namely, such a mutation strategy will end up pushing all agents towards the space origin, which happens to be the function optimal point.

This seems to be an advantage just because the "gravity point" created by the Gaussian multiplicative noise coincides with the function optima. However, if the function optima point is translated by a given constant c, GA fails to find its optima point, which also changes from 0 to c.

Let's consider the following configurations to illustrate the aforementioned arguments:

Setting 1

- n_agents = 20
- n_iterations = 50
- target_function: s(x) = np.power(x, 2).sum(), with x* = 0 and s(x*) = 0;
  - lower_bound = -10
  - upper_bound = 10

Setting 2

- n_agents = 20
- n_iterations = 50
- target_function: s'(x) = np.power(x - 2, x).sum(), with x* = 2 and s(x*) = 0
  - lower_bound = -8
  - upper_bound = 12

Notice the second setting changes lower and upper bound values to keep the same search amplitude from the first configuration. Moreover, let's consider two variations of the GA algorithm: one using multiplicative gaussian noise for mutation (as in the current implementation) and another (say, GA') that performs mutation by replacing a given agent coordinate by a random number uniformy sampled from the interval [lower_bound, upper_bound]. By combining both algorithms with each setting and running the optimization 40 times for each scenario, the following results are obtained:

Scenario Algorithm Function mean_fitness ± stddev_fitness
1 GA s(x) 0.0000 ± 0.0000
2 GA s'(x) 9.2481 ± 2.7630
3 GA' (modified) s(x) 5.9511 ± 2.6698
4 GA' (modified) s'(x) 5.1678 ± 1.7503

Results show that by using Gaussian multiplicative noise GA finds the exact optimal point for s(x) in all cases (std is zero in Scenario 1). However, when the optimal point is translated, the algorithm fails to do so (Scenario 2). Furthermore, by replacing the mutation strategy by one that has no preference over any point in the space, the results obtained either in the original sphere function (Sphere 3) or in the translated function (Scenario 4) are very close.

Comparing the results from the 40 runs between Scenarios 3 and 4 through the Wilcoxon signed-rank test it's possible to obtain p-value>0.41, meaning both results are very likely to come from the same distribution. Conversely, comparing Scenarios 1 and 2 with the same metodology yields a p-value<3e-8, meaning results' difference is statistically significant.

Last but not least, notice that values sampled from a Gaussian distribution with mean=0 and stddev=1 are negative for half of time, meaning that optimizing a function that has a positive lower bound will push agent coordinates towards this value half of the time.

Desktop (please complete the following information):

lzfelix commented 3 years ago

Just realised that I should've used t-test, not WIlcoxon, since samples are not paired. Still, the argument holds.

gugarosa commented 3 years ago

Hello @lzfelix! I hope everything is going well with you!

First of all, I would like to thank you for finding out this bug in our code. Secondly, we will take a cautious look and find another mutation strategy that do not lead to such behaviors!

Best regards, Gustavo.