Algorithm::Evolve
Algorithm::Evolve - An extensible and generic framework for executing evolutionary algorithms
#!/usr/bin/perl -w
use Algorithm::Evolve;
use MyCritters; ## Critter class providing appropriate methods
sub callback {
my $p = shift; ## get back the population object
## Output some stats every 10 generations
print $p->avg_fitness, $/ unless $p->generations % 10;
## Stop after 2000 generations
$p->suspend if $p->generations >= 2000;
}
my $p = Algorithm::Evolve->new(
critter_class => MyCritters,
selection => rank,
size => 400,
callback => \&callback,
);
$p->start;
## Print out final population statistics, cleanup, etc..
This module is intended to be a useful tool for quick and easy implementation of evolutionary algorithms. It aims to be flexible, yet simple. For this reason, it is not a comprehensive implementation of all possible evolutionary algorithm configurations. The flexibility of Perl allows the evolution of any type of object conceivable: a simple string or array, a deeper structure like a hash of arrays, or even something as complex as graph object from another CPAN module, etc.
It's also worth mentioning that evolutionary algorithms are generally very
CPU-intensive. There are a great deal of calls to rand() and a lot of
associated floating-point math. If you want a lightning-fast framework, then
searching CPAN at all is probably a bad place to start. However, this doesn't
mean that I've ignored efficiency. The fitness function is often the biggest
bottleneck.
The configurable parts of an evolutionary algorithm can be split up into two categories:
In Algorithm::Evolve, the first group of options is implemented by the user for maximum flexibility. These functions are abstracted to class of evolvable objects (a critter class in this document). The module itself handles the representation-independent parts of the algorithm using simple configuration switches and methods.
If you're of the ilk that prefers to learn things hands-on, you should probably stop here and look at the contents of the examples/ directory first.
Algorithm::Evolve maintains a population of critter objects to be evolved. You may evolve any type of objects you want, provided the class supplies the following methods:
Class->new()
Class->crossover( $critter1, $critter2 )
$critter->mutate()
$critter->fitness()
This method will also be called as an instance method, with no arguments. It should return the critter's fitness measure within the problem space, which should always be a nonnegative number. This method need not be memo-ized, as it is only called once per critter by Algorithm::Evolve.
This method may be omitted only if using gladitorial selection/replacement (see below).
Class->compare( $critter1, $critter2 )
You may also want to use the DESTROY method as a hook for detecting when
critters are removed from the population.
See the examples/ directory for example critter classes. Also, take a look at Algorithm::Evolve::Util which provides some useful utilities for implementing a critter class.
$p = Algorithm::Evolve->new( option => value, ... )
Takes a hash of arguments and returns a population object. The relevant options are:
critter_class, the name of the critter class whose objects are to be
evolved. This class should already be use'd or require'd by your code.
selection and replacement, the type of selection and replacement methods to use. Available methods for both currently include:
$p->size, while the
least-fit critter's weight is 1. For replacement, the weights are in reverse
order.
You may mix and match different kinds of selection and replacement methods. The
only exceptions are tournament and gladitorial, which must be used as
both selection and replacement method.
If both selection and replacement methods are the same, you may omit one from the list of arguments.
tournament_size, only required if you choose tournament selection/replacement. Should be at least 4 unless you know what you're doing.
max_gladitorial_attempts: Because comparisons in gladitorial selection may
result in a tie, this is the number of ties permitted before giving up and
picking critters at random instead during that breeding event. The first time
this occurs, the module will carp a message.
parents_per_gen and children_per_gen control the number of breedings per generation. children_per_gen must be a multiple of parents_per_gen. parents_per_gen must also be an even number. Each pair of parents selected in a generation will produce the same number of children, calling the crossover method in the critter class as many times as necessary. Basically, each selected parent gets a gene copy count of children_per_gen/parents_per_gen.
You may omit children_per_gen, it will default to equal parents_per_gen. If you omit both options, they will default to 2.
In tournament and gladitorial selection, children_per_gen must be equal to parents_per_gen. The number of tournaments each generation is equal to parents_per_gen/2.
size, the number of critters to have in the population.
callback, an optional (but highly recommended) reference to a function. It should expect one argument, the population object. It is called after each generation. You may find it useful for printing out current statistical information. You must also use it if you intend to stop the algorithm after a certain number of generations (or some other criteria).
random_seed, an optional number that will be fed to srand before the
algorithm starts. Use this to reproduce previous results. If this is not given,
Algorithm::Evolve will generate a random seed that you can retrieve.
$p->size()
$p->run()
suspend'ed.
$p->suspend()
run method.
$p->resume()
suspend'ed.
$p->generations()
$p->avg_fitness()
$p->best_fit()
$p->critters()
$p->fitnesses()
$p->random_seed()
$p->selection( [ $new_method ] )
$p->replacement( [ $new_method ] )
$p->parents_children_per_gen($parents, $children)
When there is no absolute measure of fitness for a problem, and a critter's fitness depends on the other memebers of the population, this is called co-evolution. A good example of such a problem is rock-paper-scissors. If we were to evolve strategies for this game, any strategy's success would be dependent on what the rest of the population is doing.
To perform such an evolutionary algorithm, implement the compare method
in your critter class and choose gladitorial selection and replacement.
Gladitorial selection/replacement chooses random pairs of critters and
compares them. If the result is not a tie, the winner receives reproduction
rights, and the loser is chosen for replacement. This happens until the
desired number of parents have been selected, or until a timeout occurs.
To add your own selection and replacement methods, simply declare them in
the Algorithm::Evolve::selection or Algorithm::Evolve::replacement
namespaces, respectively. The first argument will be the population object,
and the second will be the number of critters to choose for
selection/replacement. You should return a list of the indices you chose.
use Algorithm::Evolve;
sub Algorithm::Evolve::selection::reverse_absolute {
my ($p, $num) = @_;
## Select the indices of the $num lowest-fitness critters.
## Remember that they are sorted by increasing fitness.
return (0 .. $num-1);
}
sub Algorithm::Evolve::replacement::reverse_absolute {
my ($p, $num) = @_;
## Select indices of the $num highest-fitness critters.
return ($p->size - $num .. $p->size - 1);
}
## These are like absolute selection/replacement, but reversed, so that
## the evolutionary algorithm *minimizes* the fitness function.
my $p = Algorithm::Evolve->new(
selection => reverse_absolute,
replacement => reverse_absolute,
...
);
See the source of this module to see how the various selection/replacement methods are implemented. The mechanism for adding additional selection/replacement methods may change in future versions.
Algorithm::Evolve::Util, the examples/ directory.
Algorithm::Evolve is written by Mike Rosulek <mike@mikero.com>. Feel free to contact me with comments, questions, patches, or whatever.
Copyright (c) 2003 Mike Rosulek. All rights reserved. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.