Recurrent Connections

This tutorial introduces using the CGP-Library to create recurrent programs.

Recurrent programs are those which can contain feedback connections allowing CGP chromosomes to describe cyclic as well as acyclic graphs.  The ability to describe cyclic programs allows CGP to be applied to partially observable tasks through the ablity to hold internal state information.  Recurrent Cartesian Genetic Programming (RCGP) can create recurrent equations, boolean logic state machines and recurrent artificial neural networks.

This tutorial closely follows the Getting Started tutorial and it is recommended that you read that tutorial first.

Summary
Recurrent ConnectionsThis tutorial introduces using the CGP-Library to create recurrent programs.
The ProgramA simple program illustrating using CGP-Library to create recurrent equations.
Stepping Through the CodeA close look at the code.

The Program

A simple program illustrating using CGP-Library to create recurrent equations.

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

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

int main(void){

    struct parameters *params = NULL;
    struct dataSet *trainingData = NULL;
    struct chromosome *chromo = NULL;

    int numInputs = 1;
    int numNodes = 15;
    int numOutputs = 1;
    int nodeArity = 2;

    int numGens = 100000;
    int updateFrequency = 500;

    double recurrentConnectionProbability = 0.10;

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

    addNodeFunction(params, "add,sub,mul,div");

    setUpdateFrequency(params, updateFrequency);

    setRecurrentConnectionProbability(params, recurrentConnectionProbability);

    printParameters(params);

    trainingData = initialiseDataSetFromFile("./dataSets/fibonacci.data");

    chromo = runCGP(params, trainingData, numGens);

    printChromosome(chromo, 0);

    freeDataSet(trainingData);
    freeChromosome(chromo);
    freeParameters(params);

    return 0;
}

Stepping Through the Code

A close look at the code.

The above code is very similar to gettingStarted.c seen in the Getting Started tutorial and shall not be explained again.  Here the specifics relating to recurrent connections shall be highlighted.

By default initialiseParameters returns a pointer to an initialised parameters structure with the recurrent connection probability set to zero.  The recurrent connection probability stores the likelihood that when a connection gene is mutated it will create a recurrent connection.  Where a recurrent connection connects a node of index i to a node of index j where j>=i.  For instance a recurrent connection probability of 0.5 would cause mutations to create as many recurrent connections as feed forward.  A recurrent connection probability of 0.1 would cause mutations to create recurrent connections 10% of the time and feed forward connections 90% of the time.  Therefore the default recurrent connection probability of 0.0 results in no recurrent connections; standard acyclic CGP.

In order to use CGP to create cyclic/recurrent programs the recurrent connection probability value stored in the parameters must be altered.  This is achieved using setRecurrentConnectionProbability.

double recurrentConnectionProbability = 0.10;
...
setRecurrentConnectionProbability(params, recurrentConnectionProbability);

Once the recurrent connection probability is set to a value >0 (and <=1) CGP-Library can now create recurrent/cyclic programs.

In this example Recurrent Cartesian Genetic Programming (RCGP) is used to create a recurrent equations which generates the Fibonacci sequence.  Although the Fibonacci sequence does have a closed/explicit form it is much more easily defined recursively; making it a good application of RCGP.

For this example the function set will contain: addition, subtraction, multiplication and division.

addNodeFunction(params, "add,sub,mul,div");

The Fibonacci dataset, given in /dataSets/fibonacci.data, is also loaded into a dataSet structure.

trainingData = initialiseDataSetFromFile("./dataSets/fibonacci.data");

Since the recurrent connection probability has been set to a value greater than zero, recurrent programs can now be created using runCGP or repeatCGP; similarly to standard acyclic CGP.  In this example runCGP is used.

chromo = runCGP(params, trainingData, numGens);

After runCGP has finished the returned chromosome may describe a solution which contains recurrent connections.  It only “may” use recurrent connections as even when the recurrent connection probability is set to a non-zero value, CGP can still produce standard feed forward programs.  This is because the the recurrent connection probability only places a bias on mutations creating recurrent connections, it does not force the evolutionary process to make use of recurrent connections.  This is true except in the case where the recurrent connection probability is set to 1.00 (100%) in which case the CGP chromosomes contain nothing but recurrent connections; although this would likely never be useful.

The solution produced by runCGP is now printed to the terminal / command prompt.

printChromosome(chromo, 0);

As can be seen from the displayed chromosome, it can sometimes be difficult to follow the logic of recurrent solutions.  To help understand the operation of the produced solution some of the visualisation tools available within the CGP-Library can be used.  For further information on how to use these tools see the Visualisation tutorial.

And that’s it, by simply setting the recurrent connection probability parameter so a value greater than zero CGP-Library can be used to create recurrent programs.

This tutorial introduces and steps through a simple program which uses the CGP-Library to solve a symbolic regression problem.
DLL_EXPORT struct parameters *initialiseParameters(const int numInputs,
const int numNodes,
const int numOutputs,
const int arity)
Initialises a parameters structure used throughout the CGP-Library.
struct parameters
Stores general evolutionary and chromosome parameters used by the CGP-Library.
DLL_EXPORT void setRecurrentConnectionProbability(
   struct parameters *params,
   double recurrentConnectionProbability
)
Sets the recurrent connection probability in the given parameters.
struct dataSet
Stores a data set which can be used by fitness functions when calculating a chromosomes fitness.
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.
struct chromosome
Stores a CGP chromosome instances used by the CGP-Library.
This tutorial introduces the many ways in which CGP chromosomes can be visualised using the CGP library.
Close