Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot use Integer type with others? #31

Closed
mhucka opened this issue Jan 1, 2018 · 5 comments
Closed

Cannot use Integer type with others? #31

mhucka opened this issue Jan 1, 2018 · 5 comments
Labels

Comments

@mhucka
Copy link

mhucka commented Jan 1, 2018

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
Copy link

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
Copy link

@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
Copy link
Member

dhadka commented Jan 19, 2018

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
Copy link
Author

mhucka commented Jan 19, 2018

@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
Copy link

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.

@github-actions github-actions bot added the Stale label Nov 13, 2022
@github-actions github-actions bot closed this as not planned Won't fix, can't repro, duplicate, stale Nov 21, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants