anyoptimization / pymoo

NSGA2, NSGA3, R-NSGA3, MOEAD, Genetic Algorithms (GA), Differential Evolution (DE), CMAES, PSO
https://pymoo.org
Apache License 2.0
2.21k stars 381 forks source link

Multi-objective Optimization With Mixed Variables - size of Choice variable #368

Closed NatOnera closed 1 year ago

NatOnera commented 1 year ago

I tried to modify the example to perform Multi Objective with mixed variables (version 0.6) with 2 choices "x": Choice(options=["nothing", "multiply"]),

If I have to use the value associated to x, sometimes the last character of x is skipped (depending on the size of the float z): "multip" instead of "multiply".

{'b': True, 'x': 'multiply', 'y': 0, 'z': 0.5129253542002512}
{'b': True, 'x': 'nothing', 'y': 2, 'z': 1.142493931143185}
{'b': False, 'x': 'multipl', 'y': -1, 'z': -4.163769954529738}

here is the few lines of code to try and get the message error

import numpy as np 
from pymoo.core.problem import ElementwiseProblem 

from pymoo.algorithms.moo.nsga2 import NSGA2, RankAndCrowdingSurvival
from pymoo.core.mixed import MixedVariableMating, MixedVariableGA, MixedVariableSampling, MixedVariableDuplicateElimination
from pymoo.optimize import minimize
from pymoo.core.variable import Real, Integer, Choice, Binary

class MultiObjectiveMixedVariableProblem(ElementwiseProblem):

    def __init__(self, **kwargs):
        vars = {
            "b": Binary(),
            "x": Choice(options=["nothing", "multiply"]),
            "y": Integer(bounds=(-2, 2)),
            "z": Real(bounds=(-5, 5)),
        }
        super().__init__(vars=vars, n_obj=2, n_ieq_constr=0, **kwargs)

    def _evaluate(self, X, out, *args, **kwargs):
        print(X)
        b, x, z, y = X["b"], X["x"], X["z"], X["y"]

        f1 = z ** 2 + y ** 2

        if b:
            f2 = 100 * ((z+2) ** 2 + (y-1) ** 2)

        if x == "multiply":
            f2 = 10 * ((z+2) ** 2 + (y-1) ** 2)
        elif x == "nothing":
            f2 = (z+2) ** 2 + (y-1) ** 2
        else:
            print("error in x variable with =", x)

        out["F"] = [f1, f2]

problem = MultiObjectiveMixedVariableProblem()

algorithm = MixedVariableGA(pop_size=20, survival=RankAndCrowdingSurvival())

res = minimize(problem,
               algorithm,
               ('n_gen', 50),
               seed=1,
               verbose=False)
blankjul commented 1 year ago

I just made a commit that should fix the issue. Thank you so much for finding this bug.

What happened? Numpy uses a fixed string length of 7, whenever the array only contains nothing. When the mutation now brings up multiply, while setting it only the first 7 characters are copied over and the y is ignored. Took me quite a bit to figure this out.

Please let me know if the issue is resolved in the current master branch. If not please reopen the issue.

NatOnera commented 1 year ago

Thank you! I will try the new version.

ethantenison commented 1 year ago

Even with the latest version, I'm having the same issue! I'm using NSGA3 with MixedVariableMating, which is different from the example above. It seems to cut values so that all them in a categorical variable are the same length. For example, if the shortest value in col1 is "AA", then all the others in col1 will eventually be sliced to 2.

aaronkl commented 1 year ago

+1 I use NSGA-2 with MixedVariableMating() and MixedVariableSampling(). After 2 generations the last element in my population contains the value 'nor_' instead of 'nor_conv_1x1'. All other elements contain only valid choices.