Project-Platypus / Platypus

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

Cannot use Integer type with others? #31

Closed mhucka closed 1 year ago

mhucka commented 6 years ago

If I attempt to use a problem.types array that contains an Integer type and a Real, it runs for a while and then I get the error

platypus.core.PlatypusError: must define variator for mixed types

Unfortunately, I cannot find an explanation in the documentation about what this error means and what I can do about it. Looking at the source code, the error comes from isinstance comparisons to the class of the first element in the list problem.types. Now, the type Integer is defined as a subclass of Binary in the file types.py, and Binary is a type defined in PlatypusConfig.default_variator, so it seems like it should work and that the failure comes from not detecting the subclass relationship.

As a test of this hypothesis, I added definitions of Integer to config.py like this (copying the definitions from Binary, since Integer is based on it anyway):

class _PlatypusConfig(object):

    def __init__(self):
        super(_PlatypusConfig, self).__init__()

        self.default_variator = {Real : GAOperator(SBX(), PM()),
                                 Binary : GAOperator(HUX(), BitFlip()),
                                 Integer : GAOperator(HUX(), BitFlip()),
                                 Permutation : CompoundOperator(PMX(), Insertion(), Swap()),
                                 Subset : GAOperator(SSX(), Replace())}

        self.default_mutator = {Real : PM(),
                                Binary : BitFlip(),
                                Integer : BitFlip(),
                                Permutation : CompoundMutation(Insertion(), Swap()),
                                Subset : Replace()}
...

and then bypassed the tests for all([isinstance(t, base_type) for t in problem.types]) on lines 56 and 74 in the file config.py (by simply writing if True:). The result runs and seems to be working.

This was a hack and a correct solution is needed, but I wanted to report the problem and a possible direction to explore for fixing it.

manuparra commented 6 years ago

Well, the problem is line 54: base_type = problem.types[0].__class__ is checking just the first type of your chromosome, not all distinct types in your chromosome.

manuparra commented 6 years ago

@dhadka Could you post an example with Float and Integer and how do I add custom variators (Integer : GAOperator(HUX(), BitFlip())) for Integer inside the problem ?

By default in config.py:

self.default_variator = {Real : GAOperator(SBX(), PM()),
                                 Binary : GAOperator(HUX(), BitFlip()),
                                 Integer : GAOperator(HUX(), BitFlip()), # Added by me
                                 Permutation : CompoundOperator(PMX(), Insertion(), Swap()),
                                 Subset : GAOperator(SSX(), Replace())}

The line added by me: Integer : GAOperator(HUX(), BitFlip()), enable Integer type with HUX and BitFlip. I have done changes in operators.py for HUX and BitFlip :

class HUX(Variator):

    def __init__(self, probability = 1.0):
        super(HUX, self).__init__(2)
        self.probability = probability

    def evolve(self, parents):
        result1 = copy.deepcopy(parents[0])
        result2 = copy.deepcopy(parents[1])
        problem = result1.problem

        if random.uniform(0.0, 1.0) <= self.probability:
            for i in range(problem.nvars):
                if isinstance(problem.types[i], Binary):                    
                    for j in range(problem.types[i].nbits):
                        if result1.variables[i][j] != result2.variables[i][j]:
                            if bool(random.getrandbits(1)):
                                result1.variables[i][j] = not result1.variables[i][j]
                                result2.variables[i][j] = not result2.variables[i][j]
                                result1.evaluated = False
                                result2.evaluated = False

                if isinstance(problem.types[i], Integer):
                    ax=result1.variables[i]
                    bx=result2.variables[i]
                    result1.variables[i]=bx
                    result2.variables[i]=ax
                    result1.evaluated = False
                    result2.evaluated = False

        return [result1, result2]

Adding problem.types -> Integer:

... if isinstance(problem.types[i], Integer): ...

and after change to if True: in config.py

def default_variator(problem):
    if len(problem.types) == 0:
        raise PlatypusError("problem has no decision variables")

    base_type = problem.types[0].__class__

    if True:  #all([isinstance(t, base_type) for t in problem.types]):
       ...

I see that when running it, the Integer variables do not mutate or change, and always integer have the same values in population, but Real variables work fine (mutate, etc).

How can I make it work with Integer?

dhadka commented 6 years ago

You would need to do something like:


from platypus import *

def mixed_type(x):
    print("Evaluating", x)
    return [x[0], x[1]]

problem = Problem(2, 2)
problem.types[0] = Real(0, 10)
problem.types[1] = Integer(0, 10)
problem.function = mixed_type

algorithm = NSGAII(problem, variator=CompoundOperator(SBX(), HUX(), PM(), BitFlip()))
algorithm.run(10000)

print("Final solutions:")
for solution in unique(nondominated(algorithm.result)):
    print(solution.variables, solution.objectives)

The key here is that you need to provide a custom variator that is capable of mutating both real and int. We can use the CompoundOperator in most cases to combine operators of different types.

mhucka commented 6 years ago

@dhadka Thank you! I can confirm that this works in my case, involving 2 integers and 4 reals, with an unmodified config.py.

github-actions[bot] commented 1 year ago

This issue is stale and will be closed soon. If you feel this issue is still relevant, please comment to keep it active. Please also consider working on a fix and submitting a PR.