This tutorial introduces some of the operations which can be used to manipulate individual CGP chromosomes.
These functions are provided for three key reasons.
1) To allow evolved chromosomes to be saved and used later by other programs including their intended application.
2) To enable users to implement custom selection and reproduction schemes.
3) To enable users to create their own programs using the same chromosomes as used by the CGP-Library.
Manipulating Chromosomes | This tutorial introduces some of the operations which can be used to manipulate individual CGP chromosomes. |
The Program | A simple program showcasing a range of chromosome operations. |
Stepping Through the Code | A close look at each line of the example code. |
A simple program showcasing a range of chromosome operations.
The program below is provided in the CGP-Library download within /examples/manipulatingChromosomes.c and is included in the code:blocks project.
#include <stdio.h> #include <math.h> #include "../src/cgp.h" #define POPULATIONSIZE 5 #define NUMINPUTS 1 #define NUMNODES 8 #define NUMOUTPUTS 1 #define ARITY 2 int main(void){ struct parameters *params = NULL; struct chromosome *chromoA = NULL; struct chromosome *chromoB = NULL; struct chromosome *chromoC = NULL; struct dataSet *trainingData = NULL; double testInputs[NUMINPUTS]; params = initialiseParameters(NUMINPUTS, NUMNODES, NUMOUTPUTS, ARITY); addNodeFunction(params, "add,sub,mul,sq,cube,sin"); trainingData = initialiseDataSetFromFile("./dataSets/symbolic.data"); chromoA = initialiseChromosome(params); chromoB = initialiseChromosome(params); setChromosomeFitness(params, chromoA, trainingData); mutateChromosome(params,chromoA); copyChromosome(chromoB, chromoA); removeInactiveNodes(chromoB); printf("chromoA with inactive nodes.\n"); printChromosome(chromoA, 0); printf("chromoB without inactive nodes.\n"); printChromosome(chromoB, 0); saveChromosome(chromoB, "chromoB.chromo"); chromoC = initialiseChromosomeFromFile("chromoB.chromo"); testInputs[0] = 3; executeChromosome(chromoC, testInputs); printf("Applied input: %f\n", testInputs[0]); printf("Generated output: %f\n", getChromosomeOutput(chromoC, 0)); freeChromosome(chromoA); freeChromosome(chromoB); freeChromosome(chromoC); freeDataSet(trainingData); freeParameters(params); return 0; }
A close look at each line of the example code.
First standard headers are included along with cgp.h itself.
#include <stdio.h> #include <math.h> #include "../src/cgp.h"
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.
double testInputs[NUMINPUTS];
In order to initialise the chromosomes an initialised parameters structure with a populated function set is required.
params = initialiseParameters(NUMINPUTS, NUMNODES, NUMOUTPUTS, ARITY); addNodeFunction(params, "add,sub,mul,sq,cube,sin");
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.
mutateChromosome(params,chromoA);
Chromosomes can be copied using copyChromosome. Here chromoB is made to be a copy of chromoA.
copyChromosome(chromoB, 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.
removeInactiveNodes(chromoB);
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"); printChromosome(chromoA, 0); printf("chromoB without inactive nodes.\n"); printChromosome(chromoB, 0);
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.
saveChromosome(chromoB, "chromoB.chromo"); 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[0] = 3; executeChromosome(chromoC, testInputs); printf("Applied input: %f\n", testInputs[0]); printf("Generated output: %f\n", getChromosomeOutput(chromoC, 0));
Finally all the structures used must be free’d.
freeChromosome(chromoA); freeChromosome(chromoB); freeChromosome(chromoC); freeDataSet(trainingData); freeParameters(params);
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.
Stores general evolutionary and chromosome parameters used by the CGP-Library.
struct parameters
Initialises a chromosome based on the given parameters.
DLL_EXPORT struct chromosome *initialiseChromosome( struct parameters * params )
Sets the fitness of the chromosome using the fitness function given in the parameters
DLL_EXPORT void setChromosomeFitness( struct parameters * params, struct chromosome * chromo, struct dataSet * data )
Gets the fitness of the given chromosome
DLL_EXPORT double getChromosomeFitness( struct chromosome * chromo )
Gets the number of chromosome inputs
DLL_EXPORT int getNumChromosomeInputs( struct chromosome * chromo )
Gets the number of chromosome nodes
DLL_EXPORT int getNumChromosomeNodes( struct chromosome * chromo )
Gets the number of chromosome active nodes
DLL_EXPORT int getNumChromosomeActiveNodes( struct chromosome * chromo )
Gets the number of chromosome outputs
DLL_EXPORT int getNumChromosomeOutputs( struct chromosome * chromo )
Gets the arity of the chromosome nodes
DLL_EXPORT int getChromosomeNodeArity( struct chromosome * chromo, int index )
Mutate the given chromosome using the mutation method described in the given parameters.
DLL_EXPORT void mutateChromosome( struct parameters * params, struct chromosome * chromo )
Copies the contents of one chromoosme into the other.
DLL_EXPORT void copyChromosome( struct chromosome * chromoDest, struct chromosome * chromoSrc )
Removes all of the inactive nodes from the given chromosome.
DLL_EXPORT void removeInactiveNodes( struct chromosome * chromo )
Displays the given chromosome to the terminal / command prompt in a human readable format.
DLL_EXPORT void printChromosome( struct chromosome * chromo, int weights )
Saves the given chromosome to a file which can used to initialise new chromosomes.
DLL_EXPORT void saveChromosome( struct chromosome * chromo, char const * fileName )
Initialises a chromosome from a given previously saved chromosome.
DLL_EXPORT struct chromosome* initialiseChromosomeFromFile( char const * file )
Executes the given chromosome.
DLL_EXPORT void executeChromosome( struct chromosome * chromo, const double * inputs )
Gets the outputs of the given chromosome after it has been executed using executeChromosome.
DLL_EXPORT double getChromosomeOutput( struct chromosome * chromo, int output )