Custom Fitness Function

This tutorial introduces how to use a custom fitness functions with the CGP-Library.

Although the default supervised learning fitness function is suitable for generic application of CGP towards supervised learning tasks, there are circumstances where the you may wish to change the default fitness function.  For instance

1) To use a different measure than that used by supervisedLearning (the sum of the absolute difference between actual and desired outputs).

2) To apply CGP towards non supervised learning tasks (e.g. reinforcement learning) which require an entirely different style of fitness function.

Summary
Custom Fitness FunctionThis tutorial introduces how to use a custom fitness functions with the CGP-Library.
The ProgramA simple program showcasing how to use custom fitness functions.
Stepping Through the CodeAll custom defined fitness functions must have a prototype of the following form.

The Program

A simple program showcasing how to use custom fitness functions.

The program below is provided in the CGP-Library download within /examples/customFitnessFunction.c and is included in the code::blocks project.

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "../src/cgp.h"

double meanSquareError(struct parameters *params, struct chromosome *chromo, struct dataSet *data){

    int i,j;
    double squareError = 0;

    if(getNumChromosomeInputs(chromo) !=getNumDataSetInputs(data)){
        printf("Error: the number of chromosome inputs must match the number of inputs specified in the dataSet.\n");
        printf("Terminating.\n");
        exit(0);
    }

    if(getNumChromosomeOutputs(chromo) != getNumDataSetOutputs(data)){
        printf("Error: the number of chromosome outputs must match the number of outputs specified in the dataSet.\n");
        printf("Terminating.\n");
        exit(0);
    }

    for(i=0; i<getNumDataSetSamples(data); i++){

        executeChromosome(chromo, getDataSetSampleInputs(data, i));

        for(j=0; j<getNumChromosomeOutputs(chromo); j++){

            squareError += pow(getDataSetSampleOutput(data,i,j) - getChromosomeOutput(chromo,j), 2);
        }
    }

    return squareError / (getNumDataSetSamples(data) * getNumDataSetOutputs(data));
}


int main(void){

    struct parameters *params = NULL;

    int numInputs = 1;
    int numNodes = 20;
    int numOutputs = 1;
    int arity = 2;

    params = initialiseParameters(numInputs, numNodes, numOutputs, arity);

    setCustomFitnessFunction(params, meanSquareError, "MSE");

    printParameters(params);

    freeParameters(params);

    return 0;
}

Stepping Through the Code

All custom defined fitness functions must have a prototype of the following form.  Where the first parameter is a pointer to a parameters structure, the second is a pointer to a chromosome structure and the third is a pointer to a dataSet structure.  The use of the dataSet structure within the function is optional depending upon the fitness function.  The function returns the fitness to be assigned to the given chromosome.

double fitnessFunctionName(struct parameters *params, struct chromosome *chromo, struct dataSet *data);

Within the fitness function declaration it is recommend that a curtain level of error checking is implemented.  For instance to ensure that the given chromosome has the correct number of inputs and outputs.  This information can be acquired from the given chromosome using getNumChromosomeInputs and getNumChromosomeOutputs.  Similarly for the dataSet getNumDataSetInputs and getNumDataSetOutputs

if(getNumChromosomeInputs(chromo) !=getNumDataSetInputs(data)){
    printf("Error: the number of chromosome inputs must match the number of inputs specified in the dataSet.\n");
    printf("Terminating.\n");
    exit(0);
}

if(getNumChromosomeOutputs(chromo) != getNumDataSetOutputs(data)){
    printf("Error: the number of chromosome outputs must match the number of outputs specified in the dataSet.\n");
    printf("Terminating.\n");
    exit(0);
}

In the meanSquareError fitness function defined here the returned fitness is the mean square difference between the target and actual outputs for all inputs.

To loop through all the dataSet inputs getNumDataSetSamples is used to get the number of samples and getDataSetSampleInputs is used to get the inputs for each sample.

To calculate the chromosome outputs each set of inputs are applied using executeChromosome and the generated chromosome outputs are retrieved using getChromosomeOutput.

The chromosome outputs are then compared to the target outputs which are retrieved individually using getDataSetSampleOutput.

for(i=0; i<getNumDataSetSamples(data); i++){

    executeChromosome(chromo, getDataSetSampleInputs(data, i));

    for(j=0; j<getNumChromosomeOutputs(chromo); j++){

        squareError += pow(getDataSetSampleOutput(data,i,j) - getChromosomeOutput(chromo,j), 2);
    }
}

return squareError / (getNumDataSetSamples(data) * getNumDataSetOutputs(data));

The fitness function used by the CGP-Library is defined in a parameters structure.  Therefore in order to set the custom fitness function first a initialised parameters structure is required.

struct parameters *params = NULL;

int numInputs = 1;
int numNodes = 20;
int numOutputs = 1;
int arity = 2;

params = initialiseParameters(numInputs, numNodes, numOutputs, arity);

Now the custom fitness function is set in the parameters structure using setCustomFitnessFunction.  The first argument is the parameters structure where the fitness function will be set, the second is the custom fitness function to be set and the third is a shorthand name for the custom fitness function.

Now when runCGP or repeatCGP is called the meanSquareError fitness function shall be used in place of the default supervisedLearning fitness function.

setCustomFitnessFunction(params, meanSquareError, "MSE");

If the parameters are now displayed using printParameters the default “supervisedLearning” fitness function has been changed to “MSE”.

printParameters(params);

Finally the initialised parameters structure should be free’d.

freeParameters(params);

And that’s it, provided the custom fitness function implements the required prototype, using custom fitness functions is quite simple.  Note: in cases where the dataSet is not used by the fitness function, ‘NULL’ can be passed to runCGP or repeatCGP in place of an initialised dataSet.

struct parameters
Stores general evolutionary and chromosome parameters used by the CGP-Library.
struct chromosome
Stores a CGP chromosome instances used by the CGP-Library.
struct dataSet
Stores a data set which can be used by fitness functions when calculating a chromosomes fitness.
DLL_EXPORT int getNumChromosomeInputs(struct chromosome *chromo)
Gets the number of chromosome inputs
DLL_EXPORT int getNumChromosomeOutputs(struct chromosome *chromo)
Gets the number of chromosome outputs
DLL_EXPORT int getNumDataSetInputs(struct dataSet *data)
Gets the number of dataSet inputs.
DLL_EXPORT int getNumDataSetOutputs(struct dataSet *data)
Gets the number of dataSet outputs.
DLL_EXPORT int getNumDataSetSamples(struct dataSet *data)
Gets the number of samples in the given dataSet.
DLL_EXPORT double *getDataSetSampleInputs(struct dataSet *data,
int sample)
Gets the dataSet inputs for the given sample index.
DLL_EXPORT void executeChromosome(struct chromosome *chromo,
const double *inputs)
Executes the given chromosome.
DLL_EXPORT double getChromosomeOutput(struct chromosome *chromo,
int output)
Gets the outputs of the given chromosome after it has been executed using executeChromosome.
DLL_EXPORT double getDataSetSampleOutput(struct dataSet *data,
int sample,
int output)
Gets the dataSet output for the given sample index and output index.
DLL_EXPORT void setCustomFitnessFunction(
   struct parameters *params,
   double (*fitnessFunction)(struct parameters *params, struct chromosome *chromo, struct dataSet *data),
   char const *fitnessFunctionName
)
Set custom fitness function.
DLL_EXPORT struct chromosome* runCGP(struct parameters *params,
struct dataSet *data,
int numGens)
Applies CGP to the given task.
DLL_EXPORT struct results* repeatCGP(struct parameters *params,
struct dataSet *data,
int numGens,
int numRuns)
Repeatedly applies CGP to the given task.
DLL_EXPORT void printParameters(struct parameters *params)
Prints the given parameters to the screen in a human readable format.
Close