Re: Multiple attributes for an individual

1,441 views
Skip to first unread message

François-Michel De Rainville

unread,
Mar 23, 2012, 9:43:23 AM3/23/12
to deap-users
Hi Cristina,

From the few lines of code you sent us, I think you might want to have
a look at this tutorial
(http://deap.gel.ulaval.ca/doc/0.8-dev/tutorials/types.html#a-funky-one)
that creates an individual cycling with floats and integers.

To know exactly what the toolbox contains at anytime you can print its
content with the "dir" function:

toolbox.register("attr_int_material", random.randint, 0, 3)
toolbox.register("attr_bool_method", random.randint, 0, 1)
print(dir(toolbox))


This shall show you that both methods are registered under there given
names (None overwrote the other). You then need to use them both for
initialization (with initCycle) of your individuals.

I hope I answered your question. If not, I would need a little bit more
code and context to know exactly what you want to accomplish. (Consider
using http://pastebin.com/ or http://gist.github.com/ to send more code.)

Thanks for using DEAP,
Fran�ois-Michel

Le 12-03-23 09:08, Cristina a �crit :
> Hi all,
>
> I'm not sure how I can associate multiple atributes to an individual.
>
> For instance, I'd like an individual with a boolean and an integer
> attribute, so I did the following:
> toolbox.register("attr_int_material", random.randint, 0, 3)
> toolbox.register("attr_bool_method", random.randint, 0, 1)
>
> But I've noticed that the last register() overwrites the previous
> attribute, so the individual ends up having only the attr_bool_method
> attribute.
>
> Is this not possible at all, or am I not doing something wrong? If
> it's not possible, I will concatenate my attributes and convert them
> into a binary string representation, but I'd like to be sure that DEAP
> doesn't support multiple attributes.
>
> Thanks!
> --Cristina.

Cristina

unread,
Mar 23, 2012, 9:08:14 AM3/23/12
to deap-users

kramer65

unread,
Apr 24, 2012, 7:50:23 AM4/24/12
to deap-...@googlegroups.com
The link François-Michel provided didn't work anymore. Since I have the same question I looked for the link myself and found it here: http://deap.gel.ulaval.ca/doc/0.8/tutorials/types.html?highlight=funky#a-funky-one

I am now trying to combine the example of "a funky one" with the basic Evolution Strategy example. I messed a bit around and managed to fix several errors. But as expected, after one is fixed, a new one always pops up. Unfortunately I am now pretty stuck.

I pasted my code on pastebin here: http://pastebin.com/JKSVYwqc

The error I am getting is this:
Traceback (most recent call last):
  File "pastebin_example.py", line 104, in <module>
    main()
  File "pastebin_example.py", line 89, in main
    pop = toolbox.population(n=MU)
  File "/Library/Frameworks/Python.framework/Versions/7.2/lib/python2.7/site-packages/deap/tools.py", line 64, in initRepeat
    return container(func() for _ in xrange(n))
  File "/Library/Frameworks/Python.framework/Versions/7.2/lib/python2.7/site-packages/deap/tools.py", line 64, in <genexpr>
    return container(func() for _ in xrange(n))
TypeError: initCycle() takes at least 2 arguments (2 given)

I guess this says that I need to provide more arguments to the initCycle, but I wouldn't know what that could be.


Does anybody have any idea how I can get this to run properly? All tips are welcome!



Op vrijdag 23 maart 2012 14:08:14 UTC+1 schreef Cristina het volgende:
Op vrijdag 23 maart 2012 14:08:14 UTC+1 schreef Cristina het volgende:

Félix-Antoine Fortin

unread,
Apr 24, 2012, 10:26:57 AM4/24/12
to deap-...@googlegroups.com
Hi,

From what I understand, you want to initialize evolution strategies individuals with different 
range of values for each element of the list, and the same logic would also be applied to
strategy initialization.

If I read your intentions correctly, you do not need initCycle. You simply have to call the 
function you placed in your individual attribute list and strategy list when initializing a new
individual and a new strategy in generateES, like this :

  1. def generateES(icls, scls, attr_list, sd_list):
  2.     ind = icls(attribute() for attribute in attr_list)
  3.     ind.strategy = scls(sd() for sd in sd_list)
  4.     return ind

    The registration in the Toolbox will be like this :
    toolbox.register("individual"generateES, creator.Individual, creator.Strategy, attr_listsd_list)

The rest should stay the same.

About initCycle : first a quick reminder of the signature of initCycle :
deap.tools.initCycle(container, seq_func, n=1)

What it needs is first a container creation function (a list, an array, an individual),
then it requires a sequence of functions that will be called sequentially until the 
length of object appended to the container is equal to "n".

Now if we get back to your code, what you were trying to provide to initCycle was
a single individual produced by generateES and a value for n. The container is 
supposed to be a class, not an instantiated object, and the sequence of functions
was missing.

You could have used initCycle to instantiate your individuals, the problem is that
it would not be possible to also initialize their strategy attribute at the same time.
Here is how you could initialize the individual attributes and the strategy, then
associates the strategies with the individual :
toolbox.register("individual", tools.initCyclecreator.Individual, attr_list, n=4)
toolbox.register("strategy", tools.initCyclecreator.Strategy, sd_list, n=4)
toolbox.register("population", tools.initRepeat, listtoolbox.individual, n=100)

[...]
def main():
  1.     MU, LAMBDA = 10, 100
  2.     pop = toolbox.population(n=MU)
        for ind in pop:
            ind.strategy = toolbox.strategy()

    One last note, remember that it was always possible to play with DEAP interactively
    using the command-line interpreter (or iPython, there also iPython Notebook which is pretty cool).
    This allows you, among many things, to test your datatype creation before actually 
    calling the algorithm. This limits the chance of seeing cryptic errors, and makes thing
    easier to debug.

    Regards,
    Félix-Antoine

    Links :

kramer65

unread,
Apr 25, 2012, 7:17:32 AM4/25/12
to deap-...@googlegroups.com
Hi Félix-Antoine,

Thanks for your elaborate answer. That totally explains it. I now realise that I actually have been very close to the solution. The only thing that I missed was the opening and closing brackets '()' in the generateES function. I mean the ones below which I gave a red background:


  1. def generateES(icls, scls, attr_list, sd_list):
  2.     ind = icls(attribute() for attribute in attr_list)
  3.     ind.strategy = scls(sd() for sd in sd_list)
  4.     return ind
Although it works now I still don't really understand why they must be there. I understand that it are objects that I need to pull from the list, but in essence, they are just items from the list right? Why would they need the brackets then?

In addition to that I have two more questions:

Thanks in advance!


Op dinsdag 24 april 2012 16:26:57 UTC+2 schreef Félix-Antoine Fortin het volgende:

Félix-Antoine Fortin

unread,
Apr 25, 2012, 10:41:08 AM4/25/12
to deap-...@googlegroups.com
Hi again,

The elements you appended to "attr_list" are functions. When you call :
toolbox.register("attr_1", random.uniform, 4, 5)
you register the function and its arguments. If you enter this in a interactive console,
this is what you'll see :
>>> toolbox.attr_1
<functools.partial object at 0x10062f8e8>

We shall skip the definition of partial for now (I have included some more details at the
end of this message). All you need to know for now is that, toolbox.attr_1 is a function
which requires no arguments, and have the exact same behavior as random.uniform(4, 5).

In generateES, you first create an individual. The individual class requires at creation time
a sequence of attributes. What you currently have is a sequence of functions that generates
attributes. If you were providing a sequence of attributes, all your individual will be exactly the
same after the initialization. Therefore, you need to iterate on your list of function and generate
attributes from these functions by simpling calling them. The function will return a different random 
attribute at each call, therefore each individual will be initialized differently.

About your questions regarding array :
  • An array is basetype in Python : http://docs.python.org/library/array.html
  • When working with numeric values, the array is more efficient then a list. 
  • We use it in the examples because it makes the examples faster, and it effectively also demonstrates that deap works perfectly with arrays.
  • The typecode is required by the data type. It essentially defines what will be put in the array ('d' is for double, 'b' for byte, etc., see the link above)

If I left something unclear, do not hesitate to ask for clarifications.

Regards,
Félix-Antoine

More on partial :
A functools.partial object are provided by the Python standard library, and described here :

What we did when registering the function random.uniform, is to create a partial
object with random.uniform and its arguments, then make it an attribute of the toolbox
with the name "attr_1".

kramer65

unread,
Apr 27, 2012, 5:53:54 AM4/27/12
to deap-...@googlegroups.com
Hi Félix-Antoine,

Thanks for the elaborate answer again. Since I will be using both floats and integers in every individual the array is therefore unfortunately not possible to use for me. However, optimisation is for future consideration anyway and lists perform fine for now.

Thanks for the information on partial as well. It immediately helped me in passing more arguments in a map() function using something like this:
from functools import partial
fitnesses = toolbox.map(partial(toolbox.evaluate, data_dictionary, variable_1, variable_2, variable_3), invalid_ind)

So I worked further on my system but I now ran into a new problem involving the base.py file. I ran my system and I now get an error saying the following:

Traceback (most recent call last):
  File "es-zelluf_5.py", line 262, in <module>
    main()
  File "es-zelluf_5.py", line 202, in main
    ind.fitness.values = fit
  File "/usr/local/lib/python2.7/dist-packages/deap/base.py", line 174, in setValues
    self.wvalues = tuple(map(operator.mul, values, self.weights))
TypeError: Both weights and assigned values must be a sequence of numbers when assigning to values of <class 'deap.creator.FitnessMax'>.

So I checked my creator.fitnessMax, which is defined like this:

creator.create("FitnessMax", base.Fitness, weights=(1.0,))
creator.create("Individual", list, fitness=creator.FitnessMax, strategy=None)

As far as I understand I absolutely needed the comma after the 1.0 weight in order to make it iterable (i.e. "a sequence of numbers", even though it is only one number). But it apparently still isn't working. So I checked the base.py file but found nothing usefull (i.e. nothing that I could comprehend as being usefull). I did find a line (134) in which weights is set to None, but I guess this is done for some reason or another?

Anyway, would you have any clue as to what I am doing wrong here or how I can solve this error? All tips are welcome!

Kind regards,
Hielke

ps. I also did some tests with both DTM and the multiprocessing module one my simple quadcore machine (no cluster) and found that the multiprocessing module performs significantly better than the DTM-module. Do I understand it correctly if I say that the multiprocessing module can better be used when running on a single machine, and the DTM can better be used when running on a cluster (since mpi4py needs to be used in order to send the pickled streams around)?






Op woensdag 25 april 2012 16:41:08 UTC+2 schreef Félix-Antoine Fortin het volgende:

Félix-Antoine Fortin

unread,
Apr 27, 2012, 10:19:11 AM4/27/12
to deap-...@googlegroups.com
Hi Hielke,

Thanks for the elaborate answer again. Since I will be using both floats and integers in every individual the array is therefore unfortunately not possible to use for me. However, optimisation is for future consideration anyway and lists perform fine for now.

No problem.  
 
Thanks for the information on partial as well. It immediately helped me in passing more arguments in a map() function using something like this:
from functools import partial
fitnesses = toolbox.map(partial(toolbox.evaluate, data_dictionary, variable_1, variable_2, variable_3), invalid_ind)

In the arguments of your evaluation don't change during your evolution, you could register them in the toolbox instead of creating a partial.
toolbox.register("evaluate", evaluationFct, data_dictionary, variable_1, variable_2, variable_3)

In fact, if one your argument change, but it is a mutable type (dictionary, list, but not integer, string or float) you could register it in the toolbox even if it changes during the evolution. 
 
So I worked further on my system but I now ran into a new problem involving the base.py file. I ran my system and I now get an error saying the following:

Traceback (most recent call last):
  File "es-zelluf_5.py", line 262, in <module>
    main()
  File "es-zelluf_5.py", line 202, in main
    ind.fitness.values = fit
  File "/usr/local/lib/python2.7/dist-packages/deap/base.py", line 174, in setValues
    self.wvalues = tuple(map(operator.mul, values, self.weights))
TypeError: Both weights and assigned values must be a sequence of numbers when assigning to values of <class 'deap.creator.FitnessMax'>.

 
So I checked my creator.fitnessMax, which is defined like this:

creator.create("FitnessMax", base.Fitness, weights=(1.0,))
creator.create("Individual", list, fitness=creator.FitnessMax, strategy=None)

As far as I understand I absolutely needed the comma after the 1.0 weight in order to make it iterable (i.e. "a sequence of numbers", even though it is only one number). But it apparently still isn't working. So I checked the base.py file but found nothing usefull (i.e. nothing that I could comprehend as being usefull). I did find a line (134) in which weights is set to None, but I guess this is done for some reason or another?

The error you get has two parts : "weights must be a sequence of numbers" AND "assigned values must be a sequence". 
The evaluation function must return a tuple, or a sequence, as long as it is iterable.

This is first mentionned in the documentation here : http://deap.gel.ulaval.ca/doc/default/tutorials/start.html#operators

Now, I don't mean to be rude, we genuinely thought that the error message was correctly formated on the documentation sufficient on that matters. Could you give us some insights on where you started when reading the documentation, and if you have any tips on how we could make the error message more clear, this experience would benefit all of us :).

ps. I also did some tests with both DTM and the multiprocessing module one my simple quadcore machine (no cluster) and found that the multiprocessing module performs significantly better than the DTM-module. Do I understand it correctly if I say that the multiprocessing module can better be used when running on a single machine, and the DTM can better be used when running on a cluster (since mpi4py needs to be used in order to send the pickled streams around)?

You are right. DTM offers some advantages like the load balancing that could make the performance better in some particular even on a single machine, but in the general case on a single machine, multiprocessing is the right tool for the job.
 
Regards,
Félix-Antoine Fortin

kramer65

unread,
Apr 29, 2012, 2:39:53 PM4/29/12
to deap-...@googlegroups.com
Hi Félix-Antoine,


I must excuse myself for my last question. I did read the sequence-rule several times and I did understand I needed an iterable. When I printed the fitnesses though, I got a list of fitnesses and thought the list was the sequence I needed. I didn  realise I needed a "sequence of sequences". I simply created a list of one-tuples and that solved the issue. So it was indeed totally my (stupid) error. Thanks for your answer anyway since it got me to the solution at last.

As for your question: "if you have any tips on how we could make the error message more clear?". It would have been clearer to me if you stress that the fitnesses should be a "sequence of sequences", or if the error message had also said something like this: "This means that in the case of one fitness objective, you should have a list (or other sequence) containing a one-sized tuple for every individual.". If the docs or something else already says a line like this I apologise since I must have missed it.

I've tested my ES a couple times now and so far deap has brought me very far already. I am now in the process of going over my evaluation function (which still needs some adaptations), but the initial evolutions have already gave impressive insights!

So consider this thread (or at least my problem in this thread) as solved!


Kind regards,
Hielke



Op vrijdag 27 april 2012 16:19:11 UTC+2 schreef Félix-Antoine Fortin het volgende:
Reply all
Reply to author
Forward
0 new messages