John Mee
John Mee

Reputation: 52253

How to fix "gp.generate tried to add a primitive but there is none available"?

I'm trying to construct a typed genetic programming solution with DEAP.

I start the program with a photo of a black & white triangle and the 3 vertices of a different triangle. The hope is for the program to come up with a program which moves the vertices of the given triangle closer to the one in the photo. I provide it random constants, arithmetic primitives add, subtract, etc., if-then-else. and tests for is_black and is_white at given coordinates.

I've set up all my primitives but I keep running into this error, which seems to be telling me to add more primitives which either provide or consume (not sure which?!) the photo.

I find the error difficult because:

I'm thinking the winning program will be a long sequence of commands like "if (10,10) is black add (3,2) to vertex 1" repeated somewhat nauseatingly.

But there is perhaps something I'm not understanding correctly about how deap works. How do I overcome error messages like this?

  File "/usr/local/Cellar/python3/3.4.1/Frameworks/Python.framework/Versions/3.4/lib/python3.4/random.py", line 255, in choice
    raise IndexError('Cannot choose from an empty sequence')
IndexError: The gp.generate function tried to add a primitive of type '<class 'triangles.Photo'>', but there is none available.

This error typically fires from gp.gengrow when it is trying to generate the starting population.

What may/may not be related is that I don't have much in the way of Terminals: I'm not clear how they apply to my problem.

I expect to hear crickets, but if anyone actually takes an interest in this question and wants to see code, or at least the primitive set, I can tack it on or stick it somewhere. Figured it was rambling enough already; whilst I've focused on a specific error message I expect it is moreso my general (un)appreciation of the workings of GP/DEAP that is at fault.

Upvotes: 4

Views: 1735

Answers (2)

Nuno Andr&#233;
Nuno Andr&#233;

Reputation: 5349

I've found a fix for this problem.

When I need the input type only in terminals (as you did) I create an identity function:

pset.addPrimitive(idem, [MyClass], MyClass)

Which creates long branches of nested idems of the right depth: idem(idem(idem(x)))...

Then I stringify the primitivetree, replace all of those nested idems with a single 'x' and convert the string in a primitivetree again with deap.gp.PrimitiveTree.from_string(string, pset).

It's not elegant, it's not pythonic, but it works for me.

Upvotes: 1

John Mee
John Mee

Reputation: 52253

First I discovered that if I added a bunch of integer terminals the error disappeared. In one example I just added a bunch of integers.

for i in range(0, PLANE_SIZE):
   pset.addTerminal(i, int, name=str(i))

I found some insight to the issue in the comments here; a modified version of generate function of DEAP.

It's all about the number of layers, or depth, of the tree: genGrow is asking for something which returns the required type plus of the right depth.

Sometimes it needs an 'int' result in one layer (a terminal), and sometimes it needs an int result that consumes two layers (a function).

Hence the "NONE AVAILABLE" message: it has none available that match both the type and the depth it needs to plug into the tree.

I've haven't used it yet, but my understanding is that the gist above implements a generate function which is more flexible; it is willing to be less rigid about ensuring the tree is exactly balanced and will plug in whatever it has that fits the required type, even if it does not match the demanded depth exactly.

Upvotes: 0

Related Questions