A close look at each line of the example code.
First standard headers are included along with cgp.h itself.
A series of macros are uses to reduce the number of variables and to allow the use of arrays rather than pointers and malloc.
#define POPULATIONSIZE 5
#define NUMINPUTS 1
#define NUMNODES 8
#define NUMOUTPUTS 1
#define ARITY 2
A number of CGP-Library specific structures are used to store the CGP-LIbrary parameters, a number of chromosomes and a dataSet.
struct parameters *params = NULL;
struct chromosome *chromoA = NULL;
struct chromosome *chromoB = NULL;
struct chromosome *chromoC = NULL;
struct dataSet *trainingData = NULL;
Array to pass information into the chromosomes.
In order to initialise the chromosomes an initialised parameters structure with a populated function set is required.
params = initialiseParameters(NUMINPUTS, NUMNODES, NUMOUTPUTS, ARITY);
Here the chromosomes will be assigned a fitness based on their performance on the symbolic.data seen previously in Getting Started and Creating Data Sets.
trainingData = initialiseDataSetFromFile("./dataSets/symbolic.data");
Next we initialise two of the three chromosomes using initialiseChromosome. The initialiseChromosome function returns a pointer to an initialised chromosome using the parameters specified in params.
chromoA = initialiseChromosome(params);
chromoB = initialiseChromosome(params);
Once we have an initialised chromosome we can calculate its fitness using setChromosomeFitness. The fitness function used is specified in the parameters structure and uses the given dataSet (symbolic). In order to inspect the fitness assigned to the chromosome getChromosomeFitness is used. Other information can be accessed from the chromosome using getNumChromosomeInputs, getNumChromosomeNodes, getNumChromosomeActiveNodes, getNumChromosomeOutputs and getChromosomeNodeArity.
setChromosomeFitness(params, chromoA, trainingData);
Chromosomes can be mutated using mutateChromosome. The type and amount of mutation caused is specified in the parameters structure.
Chromosomes can be copied using copyChromosome. Here chromoB is made to be a copy of chromoA.
CGP chromosomes contain inactive nodes which can be removed using removeInactiveNodes. It should be noted that this function causes the number of chromosome nodes to change.
To see the effect of removeInactiveNodes chromoA and chromoB are printed to the terminal using printChromosome. Recall that chromoA and chromoB are identical except chromoB has had its inactive nodes removed.
printf("chromoA with inactive nodes.\n");
printf("chromoB without inactive nodes.\n");
Chromosomes can be saved to a file and read back using saveChromosome and initialiseChromosomeFromFile respectively. Here chromoB is saved to a file called “chromoB.chromo” and then read back into chromoC. It is important to node that only chromosomes which used node functions provide by the CGP-Library can be loaded using initialiseChromosomeFromFile; chromosomes containing custom node functions cannot be loaded.
chromoC = initialiseChromosomeFromFile("chromoB.chromo");
An important property of the chromosomes is than they can be used to implement the functionality they have been evolved to do. This is achieved using executeChromosome. executeChromosome takes the chromosome to be executed and an array of inputs to be applied and generates the corresponding chromosome outputs. The chromosome outputs are accessed using getChromosomeOutput. Here the chromosome take the value three as its input and displays the corresponding chromosome output to the screen.
testInputs = 3;
printf("Applied input: %f\n", testInputs);
printf("Generated output: %f\n", getChromosomeOutput(chromoC, 0));
Finally all the structures used must be free’d.
And that’s it, here we have shown how to generate, mutate, copy, save, load and execute chromosomes. With these functionalities alone it is possible to implement your own version of CGP (if you so desired). It is also possible to take the chromosomes you have evolved towards a given task and use them for their intended application.