Canonical GA with Algorithm::Evolutionary

After having dabbled with other languages for programming evolutionary computation, like C++ (for EO), or JavaScript (for GAJS), and taking a look at the EA Perl landscape, the author decided it was about time to program an evolutionary computation library in Perl. It was called initially OPEAL (for Original Perl Evolutionary Algorithm Library), but after some consultations, and previous to uploading it to CPAN (which, er, has not happened yet), it was renamed Algorithm::Evolutionary. It was intended as a complete and extensible evolutionary algorithm implementation, integrated with XML, and easy to learn, use and extend. It has been out there for about a year, and, right now, is available from SourceForge as a GPL module. Last released version is 0.5, which was released in summer 2002.

Enough hype, and let's see what the boy is able to do. Download it, do the three-phrase spell perl Makefile.PL; make; make install (and, for the wary, make test), and you'll have it installed in your preferred modules directory. You can then run this program (ea-ex1.pl):


use Algorithm::Evolutionary::Experiment;
use Algorithm::Evolutionary::Op::Easy;
my $fitness = sub {
  my $indi = shift;
  my $total = grep( $_ == 1, split(//,$indi->Chrom() ));
  return $total; 
};
my $ez = new Algorithm::Evolutionary::Op::Easy $fitness;
my $popSize = 100;
my $indiType = 'BitString';
my $indiSize = 32;
my $e = new Algorithm::Evolutionary::Experiment $popSize, $indiType, $indiSize, $ez;
print $e->asXML();
my $populationRef;
do { 
  $populationRef = $e->go();
  print "Best so far: ", $populationRef->[0]->asString(), "\n";
} until ($populationRef->[0]->Fitness() == $indiSize);
print "Final\n", $e->asXML();
. Easy as breading butter, and twice as tasty, ain't it? Well, first of all, we are not doing the canonical GA, but a steady-state GA that, each generation, substitutes 40% of the population (a default value). The program goes like this: after loading needed classes, we declare the fitness function. In Algorithm::Evolutionary, the chromosome can be any data structure, but the actual data structure evolved is always accessible via the Chrom method. In this case, that method returns a string, that is dealt with as before, to return the total number of ones. After the fitness declaration, an Algorithm::Evolutionary::Op::Easy algorithm object is created. That object is passed to another Algorithm::Evolutionary::Experiment object, which contains all stuff needed to solve the problem: algorithm, plus population. You can print that experiment object as an XML document, which would look more or less like this:

<ea xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation='ea-alpha.xsd'
    version='0.3'>
<!-- Serialization of an Experiment object. Generated automatically by
     Experiment $Revision: 1.2 $ -->
    <initial>
<op name='Easy'  ><op name='Bitflip'  rate='1' >
	<param name='howMany' value='1' />
</op>
<op name='Crossover'  rate='1' >
	<param name='numPoints' value='2' />
</op>

	<param name='selrate' value='0.4' /><code type='eval' language='perl'>
<src><![CDATA[{
    my $indi = shift @_;
    my $total = grep(($_ == 1), split(//, $indi->Chrom, 0));
    return $total;
}]]&gt;
 </src>
</code>
</op>
	</initial>
<!-- Population --><pop>
<indi type='BitString'  > <atom>0</atom> <atom>1</atom> <atom>1</atom> <atom>1</atom> <atom>1</atom> <atom>1</atom> <atom>0</atom> <atom>0</atom> <atom>0</atom> <atom>0</atom> <atom>0</atom> <atom>1</atom> <atom>1</atom> <atom>0</atom> <atom>0</atom> <atom>1</atom> <atom>0</atom> <atom>1</atom> <atom>0</atom> <atom>0</atom> <atom>1</atom> <atom>0</atom> <atom>1</atom> <atom>1</atom> <atom>0</atom> <atom>1</atom> <atom>1</atom> <atom>1</atom> <atom>0</atom> <atom>1</atom> <atom>1</atom> <atom>0</atom> 
	
</indi>
<!-- more indis like this one -->
	</pop>
</ea>
. By default, the "Easy" operator includes Bitflip mutation and crossover, each with a rate of 1 (that means that they are applied with the same probability). Each one takes a parameter, with are passed via the tag param. The Easy operator takes also a parameter, and another code section, which is converted to a subroutine of the same name as included in the attribute 'eval'; the language attribute is included for future extensions. The source code within that tag does not look exactly the same as the one above, because it has been de-parsed (using B::Deparse) from the original pointer-to-sub.

This is the kind of stuff that makes Perl unique; having a compiler/decompiler embedded in the same interpreter makes easy to serialize even complicated stuff, as data structures with pointers-to-function. I can't imagine how this could be done in C++, and it's probably impossible in Java too (Is it possible in Ruby or Python, I wonder?)

After the initial section, comes the pop section, that includes the components of the initially generated population. Each individual is enclosed by the tag indi, with a type attribute that indicates the class the individual belongs to, and them, one atom for every "atomic" component of the data structure.

That XML document can be retrieved back into a program by loading the file into a variable $xml and using this:my $experiment= Algorithm::Evolutionary::Experiment->fromXML( $xml ); .

However, as we said, this is not the canonical genetic algorithm. The program that implements it would be use the CanonicalGA class, like the example in ea-ex2.pl, which is exactly the same, except that, instead of declaring an Easy object, we declare a CanonicalGA. This object, besides implementing a canonical GA without elitism, uses QuadXOver, the crossover used before that takes two arguments by reference and returns offspring in the same arguments. The Crossover object takes arguments by value, not modifying them, and returns a single offspring.

A different problem might require a different fitness function, and probably different type of individuals. The default Algorithm::Evolutionary distribution includes four classes: Vector, for anything vectorial, from strings represented as vectors, through vector of floating point numbers, up to vectors of frobnicated foobars; String, with the BinaryString subclass, and Tree, for doing Genetic Programming or anything else that requires a tree data structure for representation. Using any of these data structures for solving a problem is left as an exercise to the student.

Algorithm::Evolutionary problems can be specified using Perl, but you can use an "universal reader" (ea-ex3.pl) to read the description of the algorithm in XML.


#!perl
use strict;
use warnings;
use Algorithm::Evolutionary::Experiment;
my $xmlDoc = join("",<>);
my $e = Algorithm::Evolutionary::Experiment->fromXML($xmlDoc);
my $populationRef = $e->go();
print "Final\n", $e->asXML();
This reader has been listed here in its entirety, but, however, since the CanonicalGA which has been used in the previous example performs a single generation, it is quite limited, and only takes you so far. That is why we need to implement a whole genetic algorithm using Algorithm::Evolutionary classes (and see how they get reflected in the XML document).