Project-Platypus / Platypus

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

EpsilonBoxArchive improvements attribute is not consistant #221

Closed gfag31 closed 6 months ago

gfag31 commented 6 months ago

In the EpsilonBoxArchive class, the "improvements" attributes is (almost) always increamented :

/platypus/core.py line 990:

            if dominated and not_same_box:
                self.improvements += 1

As long as the Archive is not empty, that the added solution is not dominated, the improvement will be increased. Is it the expected behavior ?

dhadka commented 6 months ago

Hi, thanks for the question.

Yes, this is the expected behavior. This was added back when developing the Borg MOEA, when we wanted detect when an algorithm stagnates and is no longer making significant improvements.

The not_same_box condition essentially says that the new solution must differ by more than some threshold $\epsilon$ from the solution it's dominating. If your $\epsilon$ is small enough, it could certainly evaluate to true most of the time.

More details can be found in Section 3.2 $\epsilon$-progress in http://www.mitpressjournals.org/doi/abs/10.1162/EVCO_a_00075

dhadka commented 6 months ago

You can also see this test code that shows the expected behavior - https://github.com/Project-Platypus/Platypus/blob/1a0e8cb9ea469e7e07a90c3d3974cea9fbed8336/platypus/tests/test_core.py#L280-L293

The six solutions only register two improvements given $\epsilon$ = 0.1.

  1. (0.25, 0.25) increments by 1, as it's the first solution added to the archive
  2. (0.1, 0.1) dominates (0.25, 0.25) and greater than $\epsilon$ - improvements increased by 1
  3. (0.245, 0.245) is dominated by (0.1, 0.1), no change
  4. (0.1, 0.5) is non-dominated, no change
  5. (0.5, 0.5) is dominated by (0.1, 0.1), no change
  6. (0.0, 0.0) dominates (0.1, 0.1) but is <= $\epsilon$, no change
gfag31 commented 6 months ago

Thanks for leaving the topic open and giving me a chance to reply. Sorry to insist, but if I do that simple test code (below), I don't get the same behavior as you describe :

from platypus import *

def createSolution(*args):
    problem = Problem(0, len(args))
    solution = Solution(problem)
    solution.objectives[:] = [float(x) for x in args]
    return solution

solutions = [createSolution(0.25, 0.25),      #s1
             createSolution(0.1, 0.1),        #s2
             createSolution(0.245, 0.245),    #s3
             createSolution(0.1, 0.5),        #s4
             createSolution(0.5, 0.5),        #s5
             createSolution(0.05, 0.05),      #s6
             createSolution(0.04, 0.04),      #s7
             createSolution(0.03, 0.03),      #s8
             createSolution(0.02, 0.02),      #s9
             ]

archive = EpsilonBoxArchive([0.1])

print(archive.improvements)
for s in solutions:
    res = archive.add(s)
    print(res, archive.improvements)

And I get:

0
None 0
None 1
False 1
False 1
False 1
None 2
None 3
None 4
None 5

As you can see, s6 to s9 are within epsilon, but are still seen as improvements. IMHO this is because in the line I pointed out, the boolean evaluation of an array is always True if not empty.

In my example I also included the returned value of the archive.add method. Just to point out that its evaluation will be always False => This is for borg.py line 95 (angel)

Please let me know again what are your view and thanks again.

dhadka commented 6 months ago

Ah! I see what you mean. Working on a fix...

dhadka commented 6 months ago

https://github.com/Project-Platypus/Platypus/pull/222

dhadka commented 6 months ago

I pushed another update to change the logic a bit...

  1. The first solution will now always count as an improvement
  2. Non-dominated solutions that fall within a new $\epsilon$-box also count as improvements

This matches how this calculation works in my other library, the MOEA Framework, in the EpsilonBoxDominanceArchive.

If there are any concerns with this change, please let me know! Also thank you for responding with an example showing the code was producing unexpected results!

gfag31 commented 6 months ago

Thank you for the fix. I agree that it makes sense that way. Great update !