DEAP / deap

Distributed Evolutionary Algorithms in Python
http://deap.readthedocs.org/
GNU Lesser General Public License v3.0
5.82k stars 1.13k forks source link

typed Primitive Set Index Error #219

Open JayDeng2837 opened 7 years ago

JayDeng2837 commented 7 years ago

Hi,

I was trying to formulate a problem two numpy arrays as terminals and undetermined number of random floats as ephemeral constants. (these random floats are the second input for function fmul that multiples an numpy array with a float).

The tree depth is limited to 10 and I added 300 constants, however i still get the following error.... is there a way to fix it? thanks.

IndexError: The gp.generate function tried to add a primitive of type '<type 'float'>', but there is none available.

`pset = gp.PrimitiveSetTyped("MAIN", [np.ndarray, np.ndarray],np.ndarray, 2) pset.addPrimitive(numpy.add, [np.ndarray, np.ndarray],np.ndarray, name="vadd") pset.addPrimitive(numpy.subtract, [np.ndarray, np.ndarray] ,np.ndarray, name="vsub") pset.addPrimitive(numpy.multiply, [np.ndarray, np.ndarray] ,np.ndarray, name="vmul") pset.addPrimitive(numpy.multiply, [np.ndarray, float] ,np.ndarray, name="fmul") pset.addPrimitive(numpy.negative, [np.ndarray] ,np.ndarray, name="vneg")

adding many ephemeral constants

for i in range(300): pset.addEphemeralConstant("rand%s"%i, lambda: random.randint(-1,1), float)

pset.renameArguments(ARG0='x') pset.renameArguments(ARG1='y') toolbox.register("expr", gp.genHalfAndHalf, pset=pset, min=1, max=10)`

snigdhasjg commented 5 years ago

I found out this

The generation of trees is done randomly while making sure type constraints are respected. If any primitive has an input type that no primitive and terminal can provide, chances are that this primitive will be picked and placed in the tree, resulting in the impossibility to complete the tree within the limit fixed by the generator. For example, when generating a full tree of height 2, suppose "op" takes a boolean and a float, "and" takes 2 boolean and "neg" takes a float, no terminal is defined and the arguments are booleans. The following situation will occur where no terminal can be placed to complete the tree.

Image of ### ### Yaktocat

In this case, DEAP raises an IndexError with the message "The gp.generate function tried to add a terminal of type float, but there is none available."

While adding primitive

Alleast one relation should be there for each type

Input args -> return args

  1. np.ndarray -> float
  2. float -> np.ndarray

and you were missing the 1st case

While adding terminal / ephemeral

For all primitive you have used should atleast have one terminals for them

  1. one for float
  2. one for np.ndarray

and you are missing the 2nd case

snigdhasjg commented 5 years ago

So now you have to add a dummy primitive and terminal for each of the missing case

pset = gp.PrimitiveSetTyped("MAIN", [np.ndarray, np.ndarray],np.ndarray, 2)
pset.addPrimitive(numpy.add, [np.ndarray, np.ndarray],np.ndarray, name="vadd")
pset.addPrimitive(numpy.subtract, [np.ndarray, np.ndarray] ,np.ndarray, name="vsub")
pset.addPrimitive(numpy.multiply, [np.ndarray, np.ndarray] ,np.ndarray, name="vmul")
pset.addPrimitive(numpy.multiply, [np.ndarray, float] ,np.ndarray, name="fmul")
pset.addPrimitive(numpy.negative, [np.ndarray] ,np.ndarray, name="vneg")

def dummy_primitive_for_numpyarray_to_float(numpy_array):
    # and you have handle this return in each method
    return None

pset.addPrimitive(dummy_primitive_for_numpyarray_to_float, [np.ndarray] ,float, name="dummy_primitive")

# adding many ephemeral constants
for i in range(300):
pset.addEphemeralConstant("rand%s"%i, lambda: random.randint(-1,1), float)

# dummy terminal
pset.addTerminal("dummy_terminal",lamda: None, np.ndarray)

pset.renameArguments(ARG0='x')
pset.renameArguments(ARG1='y')
toolbox.register("expr", gp.genHalfAndHalf, pset=pset, min_=1, max_=10)

You have keep in mind that all dummy terminals and primitive shouldn't get selected.

p.s. - I'm terrible at explaining things. Sorry for that.

GregoryMorse commented 2 months ago

This is a bug here: https://github.com/DEAP/deap/blob/master/deap/gp.py#L634

Basically it should not check the condition if either terminals or primitives are not available. Both not being available would be a legitimate error. I can make a PR for it.