In our second tutorial we show how to create a parallel stage and put it into the algorithm sequence, how to distribute and merge parallel
strategies, how to set an user-defined termination condition
and how to use the listeners to catch algorithm events. The objective in this tutorial problem is to find
the nearest integer array (representable with a specified number of
decimal ciphers) to a target one with each number within the range
An IntegerChromosome is a natural representation of integer array; each gene value will be constrained in the range [0,49] to respect the range constraint problem.
where MAX_INT = 49.
Similarly to the first tutorial, we extend the GeneticAlgorithm class to obtain a genetic algorithm for solving this problem. The fitness of each individual is the absolute error between its chromosome and the target chromosome. Therefore, the best individual is the one with the smallest absolute error.
The evaluate method implementation is showed below.
Let's suppose we need two different crossover operators (one point with probability 0.8 and two points
with probability 0.5) to apply them on two separated population subsets. In Jenes we can do this through the use of the Parallel class. Therefore we arrange the two crossover operators as two parallel branches
of the main pipeline.
The dispenser distributes the population between the branches of the parallel stage and merges the output of each branch in the output population of the parallel. In Jenes there are two kinds of dispencers: the general dispencer and the exclusive dispencer. The general dispencer allows, during the operation of distribution, to add an individual in more branches (copies of the same individual are needed to avoid overlapping of references between branch subpopolations). The exclusive dispencer doesn't allow it: each individual can be added at only one branch (it is not necessary to add a copy of it). We choose to use an exclusive dispencer. The implementation is showed below.
This basic implementation of an exclusive dispencer uses a simple distribution policy. It puts the individuals in even positions in the first branch and the individuals in the odd positions in the others branch. The preDistribute method is useful to reset the dispencer state. The distribute method returns the index of the branch where to put the specified individual. We should not worry about the merging of the output of the braches because it is done by the ExclusiveDispencer class. The following code shows how to create a parallel stage and insert it into the pipe.
As said before the goal of our genetic algorithm is to minimize the absolute error of the generated individuals, in order to obtain an integer array as soon as possible near to the target one.
In Jenes the genetic algorithm evolution runs until it reaches the specified generation limit or until a termination criterion is met. To specify the termination criterion our genetic algorithm has to override the "end" method. By default this method has an empty body so it has no effect on the algorithm evolution. In our example the evolution stops when the lowest fitness value is
less than a value (called precision) specified by the user.
The GeneticAlgorithm class implements the Observer pattern to notify its listeners the occurrence of a particular event. In Jenes there are two kinds of event listeners: the generation event listener (invoked when a generation ends ) and the algorithm event listener (invoked when an algorithm event occours, e.g. the start, the end and at the init of the algorithm).
In this tutorial we choose to use a generation event listener in order to print some informations at the end of each generation. The listener code is showed below
The code to add a generation event listener to the GeneticAlgorithm class is:
The following code summarises the main steps to setting up the genetic algorithm.