The Chromosome class implements most of the genetic algorithm. The this class is responsible for mating the chromosomes, performing mutations and sorting a list of chromosomes, by their suitability. Listing 8.3 shows the Chromosome class.
Listing 8.3: Display the Chromosomes (Chromosome.java) class Chromosome {
protected int [] cityList; protected double cost;
protected double mutationPercent; protected int cutLength;
Chromosome(City [] cities) {
boolean taken[] = new boolean[cities.length]; cityList = new int[cities.length];
cost = 0.0;
for ( int i=0;i<cityList.length;i++ ) taken[i] = false; for ( int i=0;i<cityList.length-1;i++ ) {
int icandidate; do {
icandidate = (int) ( 0.999999* Math.random() * (double) cityList.length ); } while ( taken[icandidate] ); cityList[i] = icandidate; taken[icandidate] = true; if ( i == cityList.length-2 ) { icandidate = 0;
while ( taken[icandidate] ) icandidate++; cityList[i+1] = icandidate; } } calculateCost(cities); cutLength = 1; }
void calculateCost(City [] cities) { cost=0;
for ( int i=0;i<cityList.length-1;i++ ) { double dist = cities[cityList[i]].proximity(cities[cityList[i+1]]); cost += dist; } } double getCost() { return cost; } int getCity(int i) { return cityList[i]; }
void setCities(int [] list) {
for ( int i=0;i<cityList.length;i++ ) { cityList[i] = list[i];
} }
void setCity(int index, int value) { cityList[index] = value;
}
void setCut(int cut) { cutLength = cut; }
void setMutation(double prob) { mutationPercent = prob;
}
int mate(Chromosome father, Chromosome offspring1, Chromosome offspring2) {
int cutpoint1 = (int)
(0.999999*Math.random()*(double)(cityList.length-cutLength)); int cutpoint2 = cutpoint1 + cutLength;
boolean taken1 [] = new boolean[cityList.length]; boolean taken2 [] = new boolean[cityList.length]; int off1 [] = new int[cityList.length];
int off2 [] = new int[cityList.length]; for ( int i=0;i<cityList.length;i++ ) { taken1[i] = false;
taken2[i] = false; }
for ( int i=0;i<cityList.length;i++ ) { if ( i<cutpoint1 || i>= cutpoint2 ) { off1[i] = -1;
off2[i] = -1; } else {
int imother = cityList[i];
int ifather = father.getCity(i); off1[i] = ifather; off2[i] = imother; taken1[ifather] = true; taken2[imother] = true; } }
for ( int i=0;i<cutpoint1;i++ ) { if ( off1[i] == -1 ) {
for ( int j=0;j<cityList.length;j++ ) { int imother = cityList[j];
if ( !taken1[imother] ) { off1[i] = imother; taken1[imother] = true; break; } } } if ( off2[i] == -1 ) {
for ( int j=0;j<cityList.length;j++ ) { int ifather = father.getCity(j);
if ( !taken2[ifather] ) { off2[i] = ifather;
taken2[ifather] = true; break;
} } } }
for ( int i=cityList.length-1;i>=cutpoint2;i-- ) { if ( off1[i] == -1 ) {
for ( int j=cityList.length-1;j>=0;j-- ) { int imother = cityList[j];
if ( !taken1[imother] ) { off1[i] = imother; taken1[imother] = true; break; } } } if ( off2[i] == -1 ) {
for ( int j=cityList.length-1;j>=0;j-- ) { int ifather = father.getCity(j);
if ( !taken2[ifather] ) { off2[i] = ifather; taken2[ifather] = true; break; } } } } offspring1.setCities(off1); offspring2.setCities(off2); int mutate = 0; if ( Math.random() < mutationPercent ) { int iswap1 = (int)
(0.999999*Math.random()*(double)(cityList.length)); int iswap2 = (int)
(0.999999*Math.random()*(double)cityList.length); int i = off1[iswap1]; off1[iswap1] = off1[iswap2]; off1[iswap2] = i; mutate++; } if ( Math.random() < mutationPercent ) { int iswap1 = (int)
(0.999999*Math.random()*(double)(cityList.length)); int iswap2 = (int)
(0.999999*Math.random()*(double)cityList.length); int i = off2[iswap1]; off2[iswap1] = off2[iswap2]; off2[iswap2] = i; mutate++; } return mutate; }
public static void sortChromosomes(Chromosome chromosomes[],int num) {
Chromosome ctemp;
boolean swapped = true; while ( swapped ) { swapped = false;
if ( chromosomes[i].getCost() > chromosomes[i+1].getCost() ) { ctemp = chromosomes[i]; chromosomes[i] = chromosomes[i+1]; chromosomes[i+1] = ctemp; swapped = true; } } } } }
The most important method provided by the Chromosome class is the mate method. This method will cause a father, which is passed in as a parameter, to mate with the mother class. We will now examine this method and see the algorithm used to combine the genetic material from two chromosomes to produce two offspring.
First a random cut point is chosen that will determine what percent of the genes, which are legs in the journey from city to city, will be taken from each parent.
int cutpoint1 = (int)
(0.999999*Math.random()*(double)(cityList.length-cutLength)); int cutpoint2 = cutpoint1 + cutLength;
Next two boolean arrays are setup so that we can remember which genes were taken from each parent. This array is initialized to false.
boolean taken1 [] = new boolean[cityList.length]; boolean taken2 [] = new boolean[cityList.length]; int off1 [] = new int[cityList.length];
int off2 [] = new int[cityList.length]; for ( int i=0;i<cityList.length;i++ ) { taken1[i] = false;
taken2[i] = false; }
The mating process is a relatively straightforward process. The mother and father chromosomes are both "cut" at two points. This divides each into three sections. These three sections are then selectively spliced back together to form two offspring. First we will handle the middle component. The following lines of code do this.
for ( int i=0;i<cityList.length;i++ ) { if ( i<cutpoint1 || i>= cutpoint2 ) { off1[i] = -1;
off2[i] = -1; } else {
int imother = cityList[i];
int ifather = father.getCity(i); off1[i] = ifather; off2[i] = imother; taken1[ifather] = true; taken2[imother] = true; } }
The middle component will be taken from the father for child 1 and the mother for child two. Each of the genes used for this section are marked as taken. Next we will loop through the left section of the chromosome. The following lines of code do this.
if ( off1[i] == -1 ) {
for ( int j=0;j<cityList.length;j++ ) { int imother = cityList[j];
if ( !taken1[imother] ) { off1[i] = imother; taken1[imother] = true; break; } } } if ( off2[i] == -1 ) {
for ( int j=0;j<cityList.length;j++ ) { int ifather = father.getCity(j);
if ( !taken2[ifather] ) { off2[i] = ifather; taken2[ifather] = true; break; } } } }
For the left section child 1 will take available genes from the mother and child 2 will take available genes from the father. Finally, the program must handle the right section. The right section works similar to the left section. The main difference is child 1 will take available genes from the father and child 2 will take available genes from the mother. The following lines of code do this.
for ( int i=cityList.length-1;i>=cutpoint2;i-- ) { if ( off1[i] == -1 ) {
for ( int j=cityList.length-1;j>=0;j-- ) { int imother = cityList[j];
if ( !taken1[imother] ) { off1[i] = imother; taken1[imother] = true; break; } } } if ( off2[i] == -1 ) {
for ( int j=cityList.length-1;j>=0;j-- ) { int ifather = father.getCity(j);
if ( !taken2[ifather] ) { off2[i] = ifather; taken2[ifather] = true; break; } } } }
The two new paths are copied to the new children. The following lines of code does this.
offspring1.setCities(off1); offspring2.setCities(off2);
Now that the parents have successfully created two offspring we may want to mutate these children slightly. The following lines of code handles the mutation process.
int mutate = 0;
if ( Math.random() < mutationPercent ) { int iswap1 = (int)
(0.999999*Math.random()*(double)(cityList.length)); int iswap2 = (int)
(0.999999*Math.random()*(double)cityList.length); int i = off1[iswap1]; off1[iswap1] = off1[iswap2]; off1[iswap2] = i; mutate++; } if ( Math.random() < mutationPercent ) { int iswap1 = (int)
(0.999999*Math.random()*(double)(cityList.length)); int iswap2 = (int)
(0.999999*Math.random()*(double)cityList.length); int i = off2[iswap1]; off2[iswap1] = off2[iswap2]; off2[iswap2] = i; mutate++; }
The mutation process is completely random. Two random genes are chosen, and then swapped as part of the mutation process. The randomness is mutation is useful to counter the very methodical mating process that is used.
The other major component of the Chromosome class is the sortChromosomes method. This method is used to sort the chromosomes according to their suitability to the problem. This allows the less desirables to be kept to the bottom of the list. The top of this list will make up the mating and preferred classes.
You have now seen a simple application of genetic algorithms. If you consider the traveling salesmen problem you will see that it is actually very similar to a neural network. There are just too many combinations to try every path in the Traveling Salesman Problem. Likewise, in a neural network, there are just too many synaptic connections to track all of them. To optimize the neural network weight matrix, we can also apply a genetic
Chapter 8: Understanding Genetic Algorithms
Article Title: Chapter 8: Understanding Genetic Algorithms Category: Artificial Intelligence Most Popular
From Series: Programming Neural Networks in Java
Posted: Wednesday, November 16, 2005 05:16 PM Author: JeffHeaton
Page: 5/5
Summary
In this chapter you were introduced to genetic algorithms. Genetic algorithms are one of the ways that programs can find potential solutions to complex NP-hard problems. A NP- hard problem is a problem where the number of steps required to solve the problem increases at a very high rate as the number of units in the program increases.
An example of an NP-hard problem, which was examined in this chapter, is the traveling salesman problem. The traveling salesmen problem attempts to seek the shortest path that a salesman would travel if he needed to visit a certain number of cities. The number of possible paths that a program would have to search increases factorial as the number of cities increases.
To solve such a problem, a genetic algorithm is used. The genetic algorithm creates a population of chromosomes. Each of these chromosomes is one path through the cities. Each leg in that journey is a gene. The best chromosomes are determined, and they are allowed to "mate". This mating process combines the genes of the two parents. The chromosomes that have longer, less desirable, paths are not allowed to mate. Because the population has a fixed size, the less desirable chromosomes are purged from memory. As the program continues, natural selection causes the better suited chromosomes to mate and produce better and better solutions.
The actual process of mating occurs by splitting the parent chromosomes into three splices. These three splices are then used to build a new chromosome. The result of all of this will be two child chromosomes. Unfortunately the mating process does not introduce new genetic material. New genetic material is introduced by mutation.
Mutation is also introduced to help find a optimal solution. One problem that arises from using only mating is that the only genetic material that will be used is that which is already in the population. Mutation randomly changes the genes of some of the newly created offspring. This introduces new traits. Many of these mutations will not be well suited and will be purged from memory. However, others can be used to move on an otherwise stagnated population.
Genetic algorithms will be used to optimize the weight matrixes of neural networks. In the next chapter you will learn another technique, to solve NP-hard problems, called simulated annealing. Finally, in Chapter 10 we will apply both genetic algorithms and simulated annealing to neural network optimization.
Chapter 9: Understanding Simulated Annealing
Article Title: Chapter 9: Understanding Simulated Annealing Category: Artificial Intelligence Most Popular
From Series: Programming Neural Networks in Java
Posted: Wednesday, November 16, 2005 05:16 PM Author: JeffHeaton
Page: 1/5
Introduction
In this chapter we will examine another technique that allows you to train neural networks. In Chapter 8 you were introduced to using genetic algorithms to train a neural network. This chapter will show you how you can use another popular algorithm, which is named simulated annealing. Simulated annealing has become a popular method of neural network training. As you will see in this chapter, it can be applied to other uses as well.
This chapter will show you how simulated annealing can be used to solve problems. The example program that is presented in this chapter solves the traveling salesman problem, just like the genetic algorithm in Chapter 8 did. This time, however, simulated annealing will be used in place of the genetic algorithm. This will allow you to see some of the advantages that simulated annealing offers over a genetic algorithm.
This chapter will begin by giving you a general background of the simulated annealing process. Next we will construct a class that is capable of using simulated annealing to solve the traveling salesman problem. Both Chapter 8 and 9 are leading up to Chapter 10, which is where you will see how both simulated annealing and genetic algorithms can be used to train a neural network. We will begin this chapter by introducing the simulated annealing algorithm.
Chapter 9: Understanding Simulated Annealing
Article Title: Chapter 9: Understanding Simulated Annealing Category: Artificial Intelligence Most Popular
From Series: Programming Neural Networks in Java
Posted: Wednesday, November 16, 2005 05:16 PM Author: JeffHeaton
Page: 2/5