[comp.ai.neural-nets] Artificial World with Neural Nets and Genetic Algorithms

ccenol@rivm05.rivm.nl (Arnold Reinders) (03/04/91)

A World full of Vehicles

Invitation to join a Neural Network competition

In this document a world will be described, in which vehicles, 
driven by small brains of neural networks drive around. Some 
die, others mate and produce offspring and still others clone. 
After a while some species will survive and others are extinct. 
This is a good test environment to test neural networks. 
Therefore we ask everybody to join us in a competition of 
neural networks. You are requested to send in your best 
performing networks according to guidelines put down in this 
document. The networks will be implemented in vehicles and 
we will see which neural network survives best. The winner will 
be announced in this newsgroup and in an article to appear in a 
major neural network magazine.
Next month the software will be ready in a library form.
Anyone who wants to test his own networks can request a copy
of this library. It will be a library of Turbo C++ routines. 
If enough people are interested a copy could be provided in
Zortech C++. Send in your home address as well, because The software
contains almost half a megabyte of information, so I'm not
sure whether I am allowed to use the net for that. In that case I will
mail a floppy.

Contact:
Arnold Reinders
RIVM (state institute for public health and environment)
Dept. ISC
P.O. Box 1
3720 BA Bilthoven
the Netherlands
email: nollie@rivm.nl

Introduction
============
Vehicles is a program that is strongly influenced by a book with 
the same name  from Valentino Braitenberg (1986). In his book 
Braitenberg explains how neural networks work by means of 
simple vehicles that inhabit an artificial world. When I once had 
to deliver a lecture about neural networks it seemed to me this 
was a good metaphor, so I wrote a simple program that 
simulated his idea's. In this program a simple world is created in 
which vehicles move around to eat mushrooms and to avoid 
cactuses.
The basic machinery that makes a vehicle move around is its 
brain. This can be a neural network of any kind (back 
propagation, ART or a Boltzmann machine). A representation of 
the external world and the internal states are mapped upon this 
brain and as a response the brain returns some action like 
moving a wheel from the vehicle.
A friend of mine and myself started to write neural networks for 
these vehicles to see who could write the best network. We had 
a lot of fun and as a result of this we ask everybody to join us in 
a competition of neural networks. The networks will be im-
plemented in vehicles and we will see which neural network 
survives best.

Warning:
Any algorithm will be considered public domain and will be 
published in this newsgroup. If you send in an algorithm, no 
copyright can be claimed, neither by the author, myself or 
anybody else. I oblige myself to publish any article in this 
newsgroup that is sent to me. In my own publications the 
names of the authors will be attached to it.

Note:
This world  was and still is intended for fun. That means that 
a lot of physical laws are interpreted in such a way that 
computation on normal PC's is minimised. Reality is quite 
irrealistic in Vehicles.

Users Guide
===========
The world in which the vehicles move is a rectangle surrounded 
by impenetrable walls. In this world vehicles, mushrooms and 
cactuses are living. As a vehicle moves around it diminishes and 
when it becomes too small it dies and becomes a mushroom. 
Whenever a vehicle stumbles upon a mushroom, this mushroom 
will be eaten. When a cactus stands in the way, the vehicle is 
wounded and declines while the cactus grows proportional in 
size.
Every type of object emits light that are characteristic for this 
object and by which it can be distinguished from other objects. 
Every object is considered to be round (they may be drawn in 
any other shape however) and its mass is pi * r * r. The different 
kind of objects will be described below.

The Vehicle
===========
A vehicle is the only moving object in this world. It contains 
sensors, two wheels and a neural network that interprets the 
world. It is perfectly round and it emits light at a wavelength of 
at least 75 (nanometers if you wish, but keep in mind: it needn't 
be realistic). Before we go into more detail of all aspects of a 
vehicle, I'll first describe how it behaves in general. In figure 2.1 
a picture is shown.
As said before, a vehicle contains two wheels, and sensors with 
which it can monitor the outside world and its internal state. The 
information that the sensors register are sent to the vehicles 
controlling brain (its neural network). The brain interprets and 
handles all signals and as a result it will send signals to, for 
example, its wheels. When it meets another vehicle, it can 
decide to mate in order to produce offspring.
In the above mentioned vehicles, two survival strategies can be 
distinguished: at first the survival of each individual vehicle. It 
tries to take decisions in such a way that it lives as long as 
possible. Another survival strategy has to do with the survival of 
the species. By choosing a useful way of reproduction (a so 
called genetic algorithm) it is possible to create a species that 
survives for a long period of time. Below we will describe 
some of the characteristics of a vehicle.
This document describes a competition. Now it is time to 
describe clearly what part of the world and vehicle is given, i.e. 
cannot be changed by the competitor and which part must be 
defined by the competitors. The world and the vehicle are pre-
defined. The vehicle is governed by physical laws which are 
defined below and which cannot be changed by anyone. The 
behaviour of the vehicle on the other side is determined by its 
brain, and this is fully defined by the competitor . Any neural 
network is allowed, like backprop, Boltzmann, ART  or a self 
defined network. This network receives input from the outside 
world via sensors, processes this information and transmits the 
results to effectors. These are interpreted by the vehicle so it can 
move along. Reproduction of the network is also fully 
determined by the competitor. 

Species of vehicles
===================
Each neural network that is sent in, is considered as a separate 
species, that means that it is assigned to a vehicle of a certain 
species. Vehicles of the same species can produce offspring, 
while vehicles of different species cannot. Species can be recog-
nized by two features. At first it is assigned a unique picture.
In this way it can be 
distinguished from other vehicles. At second it is assigned its 
own wavelength to distinguish it from other species. In this way 
the vehicles can use its photo sensors to distinguish vehicles of 
its own species from vehicles from other species.
In summary we can say that the vehicle is the carrier of the 
neural network. The vehicle is subject to predefined physical 
laws which cannot be influenced by the competitor. The neural 
network is defined by the competitor. The vehicle and the neural 
network communicate with each other by means of an interface. 
All these aspects will be described in detail below.

Sensors
=======
There are three types of sensors in the vehicle:
1. photosensors that detect light of a certain wavelength,
2. pressoreceptors that indicate whether a vehicle bumped 
   against another objects,
3. internal receptors that reflect the state of the vehicle.

Photosensors
============
The purpose of photosensors is:
- to discriminate between intensities of light,
- to discriminate between different kind of objects and
- to determine the direction of the light
The first object is reached because objects radiate light, 
depending upon size and distance of the object to the vehicle. 
The photosensors sum all values and transmit them to the neural 
network.
To discriminate between objects can be accomplished by letting 
each object emit its own wavelength of light that is unique for 
that type of objects. In table 2.1 this has been summarized:

Object type     Wave length
Mushroom        25
Cactus          50
Vehicle         >=75

Table 2.1, the wavelengths of various objects.

Each species of vehicles will be assigned its unique wavelength 
so that it can be distinguished from other vehicles. So a 
competitor has to keep in mind that his vehicle will be assigned 
a wavelength that is not known beforehand. He can distinguish 
other vehicles because they have another wavelength that is also 
not known before.
The photosensors can discriminate because there exists a row of 
sensors, each with a specific sensitivity for a certain wavelength. 
The number of photosensors is not known beforehand. A sensor 
does not sense exactly one wavelength, but senses a wavelength 
that is normally distributed around its defined wavelength and a 
standard deviation. The sensitivity of the sensors is computed as 
follows: suppose that there is a maximum bandwidth w of light 
for which photosensors  can be sensitive. Furthermore there are 
n sensors. The sensors are sensitive  at equidistant point in the 
bandwidth, so the sensitivities of sensor si can be computed by 
(2.1):

(2.1)      i * b
      si = -----
           n + 1

Suppose we have a bandwidth of 100, and 3 sensors, then the 
sensitivity of the first, second and third sensor is 25, 50 and 75 
respectively. The standard deviation sd. for each sensitivity can 
be found in 2.2:

(2.2)      1     b
      sd = - * -----
           2   n + 1.

That means that when light of a certain wavelength falls upon a 
sensor that is sensitive for another wavelength, a portion of that 
light still filters through.
The direction of light (the third objective we would like to fulfil 
with our photosensors) can be computed by setting up two 
groups of sensors at different locations so that differences in 
radiation can be attributed to distance. The photosensors are lo-
cated at the wheel.
This all boils down to the following: the intensity of the light is 
proportional to the mass of the object, so the heavier the object, 
the more light it emits. The sensor of the vehicle receives only 
some of it, that is the mass of the emitting object divided by the 
distance to that object. The intensity of light a sensor s 
registrates from object o can be put into formula (2.3) as:

(2.3)         m
       s = -------- , where d (o, s) is the Cartesian distance of
           d (o, s)   o and s.
                      
In (2.3) is m the mass of the emitting object which is equal to 
pi * r * r. Of this amount only a part is filtered that fits to the 
wavelength y (lambda). This amount f is described by formula 
(2.4) as:

(2.4)      normal (signal; y, sd)
       f = ----------------------
           normal (y; y, sd)

where b is the bandwidth (typically a number between 100 and 
1000) and n is the number of sensors (which usually varies 
between 3 and 10).
The total input of a sensor is determined by f * s. In this way an 
object can be perceived by different sensors, but each sensor 
receives another image from the perceived object. 
A last remark must be made about how light travels in this 
program, because this is most peculiar. It is not disturbed by any 
object, so for photosensors other objects are 100% transparent. 
At second, a vehicle can see at an angle of at most 90x at each 
side of the direction in which it is moving. Because it has no 
eyes in its back, it can't see what is happening behind its back.

Pressoreceptors
===============
Pressoreceptors transmit information of an impact against the 
wall, against other vehicles and cactuses to the neural network. 
The aim is to tell the brain that an impact took place and the 
speed of the impact.
The physical laws of the world do not know elasticity. What a 
vehicle should do after it has encountered a wall should be 
completely determined by its brain. It can continue to bang its 
head against the wall, or it can start to turn around to do some 
more useful things.
Pressoreceptors are located at four positions: at both wheels, the 
front and the back. Pressoreceptors cannot distinguish between 
the type of objects, only the energy of the impact is transmitted, 
which is equal to the speed of the vehicle.
Mostly the impact will not be located at exactly one 
pressoreceptor, but will be somewhere between two receptors. 
The result of the impact is proportional distributed over the two 
nearest receptors.

Internal Receptors
==================
The so called fatness indicator reflects the mass of the vehicle. 
If the vehicle has a mass that is too small , it will die and 
become a mushroom with the same mass. Whenever a vehicle 
becomes too fat, it will clone, that means create an identical 
copy of itself. Typical radiuses (masses) are in the range of (5 
(79) to 20 (1257), while a vehicle with radius 50 (7854) may 
sometimes occur. The value transmitted is the current mass 
divided by its maximum allowed mass, a number between 0 and 1.
Another internal receptor is the mass change receptor. It 
transmits positive values if the vehicle has grown (i.e. by eating) 
or negative values when it has declined since the last move. The 
value transmitted is the current mass minus the mass of the 
former move, divided by the maximum allowed mass. Again a 
number between 0 and 1.
The pleasure receptor records whether an event was pleasant or 
not for the vehicle during the last move. It is positive (+1) when 
and event was considered pleasant and negative (-1) if an event 
was experienced as unpleasant. Mating and eating are con-
sidered as pleasant events, while touching a cactus or cloning 
are considered as unpleasant.

Effectors
=========
Effectors are devices by which neural energy is transformed into 
some action, i.e. the movement of a wheel, or reflects some state 
or ability of the vehicle (for example sexual arousal).
Speed
The two most important effectors affect the vehicles wheels. By 
means of this the vehicle moves around and because it has two 
wheels, one on each side, it can turn or even move backwards.
Moving around costs energy. This energy is proportional to a 
vehicles mass and speed. How do we relate this energy to the 
mass lost by a movement. When we multiply this by a constant, 
and determine this constant Ce empirically , we have a formula 
of how to determine the mass md that is lost by making a move 
with a certain speed:

(2.5) md = Ce * m * (1 + |v|)

As we can see it takes proportional more mass the faster we 
move. The vehicle can now determine how to trade in mass for 
speed. A vehicle could decide to do so when it detects a 
mushroom and sees another vehicle speeding towards it. Notice 
that if a vehicle decides to do nothing, it still will lose some 
weight.

Arousal
=======
There is one state variable and that is sexual arousal. If a vehicle 
is aroused it can mate with other aroused vehicles and in doing 
so offspring can be produced. Mating costs energy and will 
inevitable take some of the vehicles mass away. Small vehicles 
could decide to search for mushrooms in stead of mating. This 
decision can be based upon the fatness indicator.

Reproduction
============
This chapter deals with reproduction by means of genetic 
algorithms. If you do not know genetic algorithms, don't worry, 
in the next paragraph is described what to do if you want to 
work without genetic algorithms. Maybe gives this introduction 
enough hints to experiment some yourself.
The aim of reproduction is to produce more out of two. Well... 
not exactly. It provides us with a means to provide variation into 
the world and thus to create vehicles with properties that did not 
exist before. 
When a vehicle bumps into another vehicle, and it has its sexual 
arousal indicator switched to on (as well as the vehicle bumped 
into) and they belong to the same species, they reproduce. This 
reproduction results in one child is produced, of which its brain 
is the result of a genetic algorithm. The sum of the mass of the 
parents and their child is exactly the same as that of both parents 
before.
After this the three vehicles go their own way. The crucial point 
here is that there is presumably an algorithm that creates a better 
performing neural network as output when it is given two neural 
networks as input. Such an algorithm is a so called genetic 
algorithm.
There are many genetic algorithms and I have chosen just one 
for the purpose of illustration. Suppose that all routes between 
one input neuron and one output neuron are known and that they 
must be transmitted to the child. The child will get randomly 
this connection from the one or the other parent. In this way we 
transmit the experience of one generation to the next, while 
preserving sufficient variation. We introduce some mutation, 
that means that in some cases the connection will randomly 
chosen, and not from one of the parents. In this way some new 
variation can be introduced.
When a vehicle clones, the reproduction routine is called with 
the same vehicle as both parents, so a new brain has to be 
created out of two the same brains. In practice this means that a 
same brain is created with some mutations. It is up to the 
competitor to make something out of this.
No genetic algorithm
It can be that you are not interested in the genetic algorithm 
part. In that case a network can be generated in the standard way 
(to be used for example when a vehicle is created) and transmit 
this as a result of the reproduction scheme. In this way there will 
be no learning from one generation to another. If a competitor 
does not know much about genetic algorithms, but wants to 
profit from the experience learned from a vehicle, he could copy 
the network of one of the parents chosen at random to the child.

The Mushroom
============
The mushroom is an object of the world and it emits at a 
wavelength of 25. It can be eaten by a vehicle, with the result 
that the mass of the mushroom is added to the mass of the 
vehicle. The mushroom then disappears from sight and out of 
the world.
Each mushroom grows slowly, and as it gets too heavy  it 
clones. They grow randomly, or as vehicles die, at the spot of 
the deceased vehicle appears a mushroom with the same mass of 
the deceased.

The Cactus
==========
A cactus is an object in the world that emits light at a 
wavelength of 50. A cactus is dangerous for vehicles while it 
causes them to lose mass. The amount of mass lost is randomly 
determined. The mass a vehicle loses is added to that of the 
cactus. However, contrary to mushrooms they gradually decline, 
so they depend for their living upon vehicles to bump against 
them. If a cactus becomes too small, it will die and become a 
mushroom, when it becomes too big, it will clone.

The competition
===============
This competition should also know a winner. The winner is that network
which species has the biggest amount of mass. For each species the
sum of the mass of each vehicle is determined, and the species that
has the most mass accumulated is the winner. In some pilot trials
we will look what is the amount of days for one simulation, we think
of 1000 to 10000 days.
The competition is organised according to a system of elimination.
We organise pools of species (the pool size must be determined, but
will probably be a number between 3 and 10) where each species will
have the same amount of members as the others (as well to be determined,
and probably a number between 3 and 10).
Each simulation lasts the same amount of days.

Technical documentation
=======================
The world has been written in C++, while this language allows 
for modular, well documented programs and it is rather easy to 
learn for C'ists. Each object in this world (i.e. a vehicle, 
mushroom, etc...) is a class with some public and virtual 
routines (these routines will be called methods for the rest of the 
document). Because the vehicle and the physical aspects of the 
world are predefined the user has no way to change them. 
Because in this competition a neural network and a genetic 
algorithm should be delivered, in this section I will describe 
how the interface should look like.
Each competitor is free to define his own neural network, as 
long as it obeys some rules. Neural networks that do not obey 
these rules will not be regarded in the competition. In order to 
understand these rules a short outline of how the vehicle com-
municates with the network will be given.
The time in the world is discretised, and each time step the 
vehicle determines its external and internal states, transmits 
them to the neural network, requests the network to process 
these data and reads out the output results. Based upon this 
output the vehicle makes a move. When a sexually excited 
vehicle bounces against another vehicle of the same species , the 
reproduction routine is called. Notice that the competitor cannot 
determine when the reproduction method is called, that can only 
be done by the vehicle.
The neural network must be a C++ class. It should have a public 
constructor and destructor, and the methods cycle and 
reproduce. Cycle processes the data that the vehicle itself 
places in the neural network by means of so called parameter 
blocks. The results are put into a shared piece of memory, the so 
called output parameter block, which at the end of each cycle is 
read by the vehicle to determine what its next move will be. The 
method reproduce is called to create a new network out of 
itself and its partners network.

Parameter blocks
================
Input and output is communicated to the neural network via 
parameter blocks. A parameter block is such defined that it can 
be easily extended in future. It consists of groups of parameters. 
Each parameter group is identified by its type, the amount, to be 
followed by the values of the parameter. The values that TYPE 
can assume are described in table 3.1:

Type            Meaning

PHOTO_1         Photosensor at right wheel
PHOTO_2         Photosensor at left wheel
PRESSO_1        Pressoreceptor at front
PRESSO_2        Pressoreceptor at right wheel
PRESSO_3        Pressoreceptor at back
PRESSO_4        Pressoreceptor at left wheel
FATNESS         Fatness indicator
W_CHANGED       Amount of weight changed
PLEASURE        Pleasure indicator
WHEEL_1         Right wheel
WHEEL_2         Left wheel
SEX             Sexual arousal indicator

Table 3.1, the different values of TYPE

Parameter groups cannot be accessed directly. This must be 
done via methods of the parameterblock in order to avoid errors.
A parameter block exists out of several parameter groups. The 
total number of parameter groups is determined by the method 
size of parameter block. The number of parameter groups in a 
parameter block is 1..size. 
Parameter groups are strictly topographically organised 
whenever possible, that means clock wise, starting from front. 
In this way the competitor has the possibility to compare results 
from sensors at the same location. A prototype of class 
parameter block is defined in figure 3.1.

class Parameter_Block
{
   int parameters ();
   int size ();
   int group_size (int index);
   int set_parameter (int index, int par, float value);
   int get_parameter (int index, int par, float& value);
};/*** Class: Parameter_Block ***/

 Figure  3.1, Prototype of class Parameter_Block

The input parameter block
=========================
The input parameter block is a piece of memory that the vehicle 
and the neural network share. The vehicle puts values into it 
while the neural network reads them. It is passed to the network 
during set-up, that means as the first parameter to the construc-
tor. In this input parameter block the following parameter 
groups are currently defined (see table 3.2):

Size    Type            Location        Meaning

1       PRESSO_1        front           Pressoreceptor at front
n       PHOTO_1         right wheel     Photosensor at right wheel
1       PRESSO_2        right wheel     Pressoreceptor at right wheel
1       PRESSO_3        back            Pressoreceptor at back
n       PHOTO_2         left wheel      Photosensor at left wheel
1       PRESSO_4        left wheel      Pressoreceptor at left wheel
1       FATNESS         internal        Fatness indicator
1       W_CHANGED       internal        Amount of weight changed
1       PLEASURE        internal        Pleasure indicator

Table  3.2, the layout of the input parameter block

By means of the get_parameter method, values can be read 
from the input parameter block. Suppose we got the following 
parameter block transmitted through the constructor of the 
neural network:

 Parameter_Block* in;

and suppose that we want to read out the values of both photo 
sensors. At first we have to determine the size of the sensors. 
This can be done by the method group_size. It returns the 
size of the requested parameter. We can now read the values of 
the photo sensors:

int n = in->group_size (PHOTO_1); // Size of sensor
float* foto1 [n + 1];             // Declare array to store values

for (int i = 1; i <= n; i++) // Loop to read out values
{                            // of right wheel
 in->get_parameter (PHOTO_1, i, foto1 [i]);
}

Fig 3.2, Reading the input parameter block

In the above mentioned example we have copied the values of 
the photo sensors to an array in our neural network. Because we 
use the built-in methods for requesting parameters and their 
values this piece of routine is insensitive for changes in the 
general build-up of the vehicle. So if the amount of photo 
sensors changes, and that will certainly happen, this network 
piece of code will not suffer because of it.

The output parameter block
==========================
Like the input parameter block, the output is a piece of memory 
that is shared by  vehicle and network alike. The network sets 
values in this block and it is read by the vehicle in order to 
determine its next move. To avoid disruption of the output 
parameter, they are hidden. Output parameters can be set by the 
following method:

 int Set_Parameter (int index, int par, float value)

A zero is returned if no problems were encountered, a one if the 
denoted parameter did not exist. The parameter group accessed 
by index indicates the parameter group to be accessed, pa-
rameter indicates the index in the group and value is the 
value the indicated parameter is to be set to. The following 
output parameters do exist:

Size    Type            Location        Meaning

1       WHEEL_1         Right wheel     Distance wheel turns
1       WHEEL_2         Left wheel          "      "     "
1       SEX             Internal        If the value is 
                                        positive the vehicle 
                                        is aroused, else not

 Table 3.3 The definition of the output parameter block

Suppose we have computed in our cycle routine new positions 
for the wheels, and have stored these values in wheel1 and 
wheel2, than we can transmit these values to the output 
parameter block as follows:

float wheel1, wheel2;

// Let the network compute values for wheel1 and wheel2.

out->set_parameter (WHEEL_1, 1, wheel1);
out->set_parameter (WHEEL_2, 1, wheel2);

Fig 3.3, Setting the output parameters

The network and the vehicle share the same copy of the 
parameter blocks. When defining a neural network, these two 
parameter blocks are the only parameters that are transmitted to 
the network via the constructor. The vehicle sets the input 
parameter block, let the network cycle and requests values of 
from the output parameter block.
In figure 3.4 a prototype of class network is given. In this 
network there are only three public routines: the constructor and 
the methods cycle and reproduce. These methods will be 
discussed in detail in the next paragraphs.

class Neural_Network
{
public:

 Neural_Network (Parameter_Block*, Parameter_Block*);
 ~Neural_Network ();
 virtual void cycle ();
 virtual Neural_Network* reproduce
  (Neural_Network* partner, // The other parent
   Parameter_Block* in,  // Input parameters
   Parameter_Block* out); // Output parameters
};/*** Class: Neural_Network ***/
 
Figure 3.4, Prototype of class Neural_Network

The Constructor
===============
Prototype: network (Parameter_Block* input, Parameter_Block* output)

Parameters:
 input  The parameter block through which the input is 
        transmitted from the vehicle to the network.
 output The parameter block through which the output of 
        the network is transmitted to the vehicle. It is set 
        by the network.

Description:
The constructor sets up the network. Relevant 
information as to the size of the network needed 
and the construction of the vehicle should be 
derived from the parameter block.

The Cycle Method
================
Prototype: void cycle ()

Description:
This method reads the input parameter block, 
does an update of the neural network and 
transmits the results to the output parameter 
block. 

The Reproduction
================
Prototype: network* reproduce (network* other, 
                               Parameter_Block* in,
                               Parameter_Block* out)

Parameters:
 other  Other is a pointer to a network of the vehicle the 
        owner of this network bounced into.
 in     Pointer to input parameter. Will be provided by 
        the vehicle.
 out    Pointer to output parameter. Will be provided by 
        the vehicle.

Results:
A pointer of a newly produced network that will 
be used for the child of both networks.

Description:
The reproduce method is part of a network and 
will be called when a reproduction should take 
place. The first parameter is a pointer to another 
network that together with the calling network 
provided the input for the reproduction. The other 
two parameters are pointers to the input and 
output parameter block that should be transmitted 
to the constructor of the neural network.
How to reproduce is completely free to the user 
as long as it results in a new network. A pointer 
to this new network must be returned.
Whenever you are not interested in this part, you 
could simply state the following in your routine:

 return new Neural_Network (in, out);

An Example
==========
As an example for a neural network I have chosen for a simple 
feed forward network with a Hebbian learning rule. Below first 
these two things will be explained, and next to that an outline of 
the data and method structure will be given. Finally,
I will give the complete listing of the class as will be 
accepted by the program.

The Neural Network
==================
The network consists out of an input layer, a hidden layer and 
an output layer. Input is mapped from the input parameter block 
upon the input layer. The input is processed by the units if the 
input layer and transmitted to the hidden layer. The hidden layer 
transmits the information to the output layer where it is mapped 
upon the output parameter block.

The Input Layer
===============
The input layer consists out of a number of units that is equal to 
the amount of input parameters and output parameters. This is 
done in order to create some feedback because not only the 
input sensors will be mapped upon the input layer, but the 
output effects are mapped as well. In this way the network is 
able to associate some input with former behaviour. Each unit 
transmits input to the hidden layer via a sigmoid function. That 
means that the activation of each unit is in the interval [0..1].

The Hidden Layer
================
The hidden layer consists of half of the units of the input layer. 
It transmits information to the output layer via the sigmoid 
function.

The Output Layer
================
The output layer consists of a number of units that is determined 
by the amount of effectors needed. The output receives the 
signals from the hidden layer. The sum of these signals is not 
affected by some transfer function but immediately mapped 
upon the output parameter block. However, for comparing the 
values of the hidden with the output the activity computed by 
the sigmoid function is used.

Weights and Learning
====================
Weights in this demonstration model lie in the interval [-1..+1]. 
When they are adjusted by the learning rule they will never 
exceed these values.
Learning is some special case of the Hebbian learning rule. It is 
assumed that when units contain the same activation, that their 
weights should not be adjusted. That means that the bigger the 
difference in activations, the bigger the adjustment to the 
weights should be. The weight will be changed in the direction 
determined by the difference in activations, while the amount of 
change is governed by this change as well. This amount 
however, is multiplied with a small value, epsilon, in order to 
keep down the change. Let wij be the weight between unit ui 
and uj and ai and aj be the activation of ui and uj respectively. 
We can then determine the direction of change by ai - aj. This is 
immediately the amount of change. In order to avoid overflow 
of the weight, we must look to the amount of weight available, 
that is 1 - |wij|. Second, we want to have a slow change, so we 
multiply this with a small number e(psilon). This result in 
formula (4.1):

(4.1) delta wij = e * (1 - |wij|) * (ai - aj)

The Genetic Algorithm
=====================
When a vehicle meets another vehicle and they are both sexual 
aroused, the two will generate a neural network for their child. 
This network is generated by the method reproduce 
(Neural_Network*). There are many ways of creating such 
a network. The method we have described before will now be 
algorithmically explained.
Between each unit in the input layer and each unit in the output 
layer exist a lot of paths. Such a path will be randomly inherited 
from either parent. In about 10% of the cases the weight will be 
randomly generated, in order to create some mutation.

Outline of Class Neural_Network
===============================
Bases upon the former discussions and upon the requirements of 
the competition, we can make a prototype the neural network as 
follows:

#include <stdio.h>

#include "intface.hpp" // Must be included: will 
described below

class Neural_Network
{
protected:

   float* F1;            // Sensor layer
   float* F2;            // Hidden layer
   float* F3;            // Effector layer
   float** W12;          // Weight matrix from 1 to 2
   float** W23;          // Weight matrix from 2 to 3
   int n1, n2, n3;       // Size of F1, F2 and F3
   Parameter_Block* IN;  // Pointer to input parameter
   Parameter_Block* OUT; // Pointer to output parameter

   float sigmoid (float);
   virtual void get_input ();
   virtual void set_output ();
   virtual void learn (float);

public:

   Neural_Network (Parameter_Block*, Parameter_Block*);
   ~Neural_Network ();
   virtual void cycle ();
   virtual Neural_Network* reproduce (Neural_Network*, 
                                      Parameter_Block*,
                                      Parameter_Block*);
};/*** Class: Neural_Network ***/
 
Neural_Network::Neural_Network (Parameter_Block* in,
                                Parameter_Block* out)

// Initialisation of the network

{
   int i, j;

   IN  = in;
   OUT = out;
   n1  = IN->size () + OUT->size ();
   n2  = n1 / 2;
   n3  = OUT->size ();
   F1  = new float [n1];
   F2  = new float [n2];
   F3  = new float [n3];
   W12 = new float* [n1];
   W23 = new float* [n2];
   for (i = 0; i < n1; i++)
   {
      W12 [i] = new float [n2];
      for (j = 0; j < n2; j++) W12 [i] [j] = uniform (-0.1, 0.1);
   }
   for (i = 0; i < n2; i++)
   {
      W23 [i] = new float [n3];
      for (j = 0; j < n3; j++)
        W23 [i] [j] = uniform (-0.1, 0.1);
   }
}/*** Neural_Network ***/

Neural_Network::~Neural_Network ()
{
   int i;

   delete F1;
   delete F2;
   delete F3;
   for (i = 0; i < n1; i++) delete W12 [i];
   delete W12;
   for (i = 0; i < n2; i++) delete W23 [i];
   delete W23;
}/*** ~Neural_Network ***/

float Neural_Network::sigmoid (float x)
{
   return 2.0 / (1.0 + exp (x)) - 1.0;
}/*** sigmoid ***/

void Neural_Network::connect12 (int i, int j, float weight)
{
   if (i >= 0 && i < n1 && j >= 0 && j < n2)
   W12 [i] [j] = weight;
}/*** connect ***/

void Neural_Network::connect23 (int i, int j, float weight)
{
   if (i >= 0 && i < n2 && j >= 0 && j < n3)
   W12 [i] [j] = weight;
}/*** connect ***/

void Neural_Network::get_input ()
{
   float value;
   int j, par;
   int count = 0;

// Map input parameters upon first layer of the network

   for (par = 0; par < IN->parameters (); par++)
   {
      for (j = 0; j < IN->group_size (par); j++)
      {
         if (IN->get_parameter (par, j, value) != OK)
         {
// Gets here when a parameter was not recognized by the
// parameter block.
            printf ("Get_parameter () not OK");
         }
         F1 [count] = sigmoid (value);
         count++;
      }
   }

// Map the output parameters upon the F1 layer as a
// way of feedback.

   for (par = 0; par < OUT->parameters (); par++)
   {
      for (j = 0; j < OUT->group_size (par); j++)
      {
         if (OUT->get_parameter (par,j,F1 [count]) != OK)
         {
// Gets here when a parameter was not recognized by the
// parameter block.
            printf ("Get_parameter didn't return OK");
         }
         count++;
      }
   }

// Adjust some values to get a higher discrimination.
// Add first at some
// noise so that always some behaviour is performed.

   for (j = 0; j < n1; j++)
   F1 [j] += uniform (-0.25, 0.25);
}/*** get_input ***/

void Neural_Network::set_output ()
{
   int j, par;
   int count = 0;

   for (par = 0; par < OUT->parameters (); par++)
   {
      for (j = 0; j < OUT->group_size (par); j++)
      {
         if (OUT->set_parameter (par,j,F3 [count]) != OK)
         {
            printf ("set_parameter didn't return OK");
         }
         count++;
      }
   }
}/*** set_output ***/

void Neural_Network::learn (float epsilon)
{
   int i, j;

   for (i = 0; i < n1; i++)
   for (j = 0; j < n2; j++)
   {
      W12 [i] [j] += epsilon * (F1 [i] - F2 [j])
       * (1.0 - fabs (W12 [i] [j]));
   }
   for (i = 0; i < n2; i++)
   for (j = 0; j < n3; j++)
   {
      W23 [i] [j] += epsilon * (F2 [i] - F3 [j])
       * (1.0 - fabs (W23 [i] [j]));
   }
}/*** learn ***/

void Neural_Network::cycle ()
{
   float sum;
   int i, j;

   get_input ();
   for (j = 0; j < n2; j++)
   {
      sum = 0.0;
      for (i = 0; i < n1; i++)
      {
         sum += F1 [i] * W12 [i] [j];
      }
      F2 [j] = sigmoid (sum);
   }
   for (j = 0; j < n3; j++)
   {
      sum = 0.0;
      for (i = 0; i < n2; i++)
      {
         sum += F2 [i] * W23 [i] [j];
      }
      F3 [j] = sigmoid (sum);
   }
   set_output ();
   learn (0.01);
}/*** cycle ***/

Neural_Network* Neural_Network::reproduce
(
   Neural_Network* partner,// Other network as parent
   Parameter_Block* in,    // Input parameter block from new vehicle
   Parameter_Block* out    // Output parameter block from new vehicle
)

// Generates a new neural network from the
// current one and the supplied
// partner network. The parameter blocks are
// transmitted as they are supplied
// from the new vehicle.

{
   Neural_Network* NN = new Neural_Network (in, out);
   float mutation = 0.1; // Mutation of about 10%
   int i, j, k;

   for (i = 0; i < n1; i++)
   for (j = 0; j < n3; j++)
   for (k = 0; k < n2; k++)
   if (uniform (0.0, 1.0) < mutation)
   { // Mutation of the weights
      NN->connect12 (i, k, uniform (-1.0, 1.0));
      NN->connect23 (k, j, uniform (-1.0, 1.0));
   } else
   {
      if (uniform (0.0, 1.0) < 0.5)
      { // Weights from parent one in half of the cases
         NN->connect12 (i, k, W12 [i] [k]);
         NN->connect23 (k, j, W23 [k] [j]);
      } else
      { // Else the weights of parent two
         NN->connect12 (i, k, partner->W12 [i] [k]);
         NN->connect23 (k, j, partner->W23 [k] [j]);
      }
   }
   return NN; // Return pointer to new neural network
}/*** reproduce ***/

Intface.hpp
===========
The intface header file provides a lot of definitions and 
prototypes which must be used. You should include it in your 
own source. In order to make it easier, the complete source is 
given below.

const int OK                  = 0;
const int ILLEGAL_INDEX       = 1;
const int ILLEGAL_PARAMETER   = 2;
const int PHOTO_1             = 0;
const int PHOTO_2             = 1;
const int PRESSO_1            = 2;
const int PRESSO_2            = 3;
const int PRESSO_3            = 4;
const int PRESSO_4            = 5;
const int W_CHANGED           = 6;
const int FATNESS             = 7;
const int PLEASURE            = 8;
const int SEX                 = 0;
const int WHEEL_1             = 1;
const int WHEEL_2             = 2;

class Parameter_Block;

class Parameter_Group
{
friend class Parameter_Block;

private:

   int SIZE;
   float* VALUE;

   Parameter_Group (int size);
   ~Parameter_Group ();
};/*** Class: Parameter_Group ***/

class Parameter_Block
{
private:

   int PARAMETERS;
   Parameter_Group** PARAMETER;

public:

   Parameter_Block (int*, int);
   ~Parameter_Block ();
   int parameters ();
   int size ();
   int group_size (int index);
   int set_parameter (int index, int par, float value);
   int get_parameter (int index, int par, float& value);
};/*** Class: Parameter_Block ***/