Custom Node Functions

This tutorial introduces how to use custom node transfer functions not provided by the CGP-Library.

Although many node functions are provided by the CGP-Library, see addNodeFunction, it may be the case that the user wishes to use their own custom node functions.  This tutorial demonstrates how to add custom node functions to the function set made available to the chromosomes so they can be used by the CGP-Library.

Summary
Custom Node FunctionsThis tutorial introduces how to use custom node transfer functions not provided by the CGP-Library.
The ProgramA simple program demonstrating how to create and add a custom node function to the function set.
Stepping Through the CodeWhen using a custom node function it is essential that the new node function has a prototype of the form

The Program

A simple program demonstrating how to create and add a custom node function to the function set.

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

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

double hypotenuse(const int numInputs, const double *inputs, const double *connectionWeights){

    int i;
    double sumOfSqrs = 0;
    double hypt;

    for(i=0; i<numInputs; i++){
        sumOfSqrs += pow(inputs[i], 2);
    }

    hypt = sqrt(sumOfSqrs);

    return hypt;
}


int main(void){

    struct parameters *params = NULL;

    int numInputs = 2;
    int numNodes = 10;
    int numOutputs = 1;
    int arity = 3;

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

    addNodeFunction(params, "add,sub");

    addCustomNodeFunction(params, hypotenuse, "hypt", -1);

    printParameters(params);

    freeParameters(params);

    return 0;
}

Stepping Through the Code

When using a custom node function it is essential that the new node function has a prototype of the form

double functionName(const int numInputs, const double *inputs, const double *connectionWeights);

This ensures that the CGP-Library knows how to use the custom node function.  The function inputs are the number of node inputs, an array of the actual node input values and an array of the connection weight associated with each input.  The connections weights can be ignored internally by the function if the custom node function is not to be used by neural networks.  The output of the custom node function is the output generated by the node for the applied inputs.

Here a hypotenuse node function has been defined which returns the hypotenuse of the inputs.  The connections weights are not used.

double hypotenuse(const int numInputs, const double *inputs, const double *connectionWeights){

    int i;
    double sumOfSqrs = 0;
    double hypt;

    for(i=0; i<numInputs; i++){
        sumOfSqrs += pow(inputs[i], 2);
    }

    hypt = sqrt(sumOfSqrs);

    return hypt;
}

In order to use our newly defined node function we first need a parameters structure to store the function in the function set.

struct parameters *params = NULL;

int numInputs = 2;
int numNodes = 10;
int numOutputs = 1;
int arity = 3;

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

As usual this parameters structure can contain a function set of pre-defined node functions.

addNodeFunction(params, "add,sub");

However the newly defined hypotenuse function can also be added using addCustomNodeFunction.  Where params stores all of the node functions in the function set, hypotenuse is the node function to be added and “hypt” is a given user defined shorthand for the hypotenuse node function.  The final parameter limits the maximum number of inputs to the function.  For instance an absolute function would only take one input and so the number of inputs would be limited to ‘1’.  In cases where a function can take any number of inputs the value of ‘-1’ is used.

addCustomNodeFunction(params, hypotenuse, "hypt", -1);

In order to see that the hypotenuse node function has been added the entire function set can be displayed using printParameters.

printParameters(params);

And that’s it, the hypotenuse node function has been added to the function set stored in parameters and is now available for use.  However it is important to note that any chromosome which uses custom node functions cannot be loaded from a file using initialiseChromosomeFromFile.

DLL_EXPORT void addNodeFunction(struct parameters *params,
char const *functionNames)
Adds pre-defined node function(s) to the set of functions stored by a parameters structure.
DLL_EXPORT void addCustomNodeFunction(
   struct parameters *params,
   double (*function)(const int numInputs, const double *inputs, const double *weights),
   char const *functionName,
   int maxNumInputs
)
Adds custom node function to the set of functions stored by a parameters structure.
DLL_EXPORT void printParameters(struct parameters *params)
Prints the given parameters to the screen in a human readable format.
DLL_EXPORT struct chromosome* initialiseChromosomeFromFile(char const *file)
Initialises a chromosome from a given previously saved chromosome.
Close