Implementation of the ACO (Ants Colony Optimization) for the traveling salesman problem. JOGL used
Assignment repository for AI course at Inha University
Problem choice: metaheuristic algorithm “Ant colony optimization”
Clone repository:
$ git clone git://github.com/xtenzQ/Uni-AI.git
To run the application, you need:
After NetBeans installation, you should install JOGL plugin:
Tools
Plugin
Downloaded
Add Plugins...
GLSL editor
.In the ant colony optimization algorithms, an artificial ant is a simple computational agent that searches for good solutions to a given optimization problem. To apply an ant colony algorithm, the optimization problem needs to be converted into the problem of finding the shortest path on a weighted graph. In the first step of each iteration, each ant stochastically constructs a solution, i.e. the order in which the edges in the graph should be followed. In the second step, the paths found by the different ants are compared. The last step consists of updating the pheromone levels on each edge. —Wikipedia
Pseudocode:
procedure ACO_MetaHeuristic
while(not_termination)
generateSolutions()
daemonActions()
pheromoneUpdate()
end while
end procedure
Each ant needs to construct a solution to move through the graph. To select the next edge in its tour, an ant will consider the length of each edge available from its current position, as well as the corresponding pheromone level.
The process of updating pheromones is slightly different from the one which is commonly used. I decided to update pheromone level on all the roads by multiplying the current level with evaporation coefficient and the update of pheronome on the egdes depends on its length. The longer the way, the less pheromone is added to the graph edge.
update()
function, Data.java
file):
private static void update() {
isData = true;
transitionsMatrix = new double[cities.size()][cities.size()];
colorMatrix = new int[cities.size()][cities.size()];
for (int i = 0; i < cities.size(); i++) {
for (int j = i + 1; j < cities.size(); j++) {
double[] first = cities.get(i);
double[] second = cities.get(j);
// fill edge matrix with its length
transitionsMatrix[i][j] = transitionsMatrix[j][i] = (Math.hypot(second[0] - first[0], second[1] - first[1]));
colorMatrix[i][j] = Data.defaultColor;
}
//
transitionsMatrix[i][i] = 0;
colorMatrix[i][i] = Data.defaultColor;
}
Algorithm.newMatrix(transitionsMatrix);
}
In newMatrix()
method transitionMatrix
is assigned to waysLength
which represents the road length between cities.
iterate()
function, Algorithm.java
file)
ants.forEach((ant) -> {
ant.newTravel();
});
by assigning ants to random cities
/**
* Send ant to new city
*/
public final void newTravel() {
chainLength = 0;
for (int i = 0; i < numOfCities; i++) {
visited[i] = false;
}
// init ant in a random city
currentCity = citiesChain[0] = random.nextInt(numOfCities);
visited[currentCity] = true;
}
iterate()
function, Algorithm.java
file)
for (currentTransition = 1; currentTransition <= numOfCities; currentTransition++) {
ants.forEach((ant) -> {
ant.selectCity();
});
}
2.1. There’s a possibility for an ant to travel to a random city (selectCity()
function, Algorithm.java
file)
// Possibility of a transition to a random city
int randCity = random.nextInt(numOfCities);
//
if (random.nextDouble() < randomFactor) {
//
if (!visited[randCity]) {
visit(randCity);
return;
}
}
2.2. Otherwise ant is going to city based on probabilities (selectCity()
function, Algorithm.java
file)
// Calculate transition probabilities
double[] transitionProbabilities = calcProbabilities();
// Transition to city
double rand = random.nextDouble();
double total = 0;
for (int i = 0; i < numOfCities; i++) {
total += transitionProbabilities[i];
if (total >= rand) {
visit(i);
return;
}
}
calcProbabilities()
function:
/**
* Calculate probability of ant travel
*
* @return
*/
private double[] calcProbabilities() {
double[] transitionProbabilities = new double[numOfCities];
// if it's last city
if (currentTransition == numOfCities) {
for (int i = 0; i < numOfCities; i++) {
// we stop
transitionProbabilities[i] = 0;
}
// get back to start city
transitionProbabilities[citiesChain[0]] = 1;
return transitionProbabilities;
}
// calculate according to formula
double sumProbalities = 0.0;
for (int i = 0; i < numOfCities; i++) {
if (visited[i]) {
transitionProbabilities[i] = 0;
} else {
//
transitionProbabilities[i] = Math.pow(getWaysPheromone()[currentCity][i], alpha) * Math.pow(1.0 / waysLength[currentCity][i], beta);
}
sumProbalities += transitionProbabilities[i];
}
for (int i = 0; i < numOfCities; i++) {
transitionProbabilities[i] /= sumProbalities;
}
return transitionProbabilities;
}
iterate()
function, Algorithm.java
file)
updatePheromone();
3.1. Evaporate pheromone from every edge by multiplying evaporation coefficient with existing pheromone level on the edge (updatePheromone()
function, Algorithm.java
file)
// evaporate pheromone
for (int i = 0; i < numOfCities; i++) {
for (int j = 0; j < numOfCities; j++) {
getWaysPheromone()[i][j] *= evaporation;
}
}
3.2. Add pheromone to every edge ant went through. The longer the way, the less pheromone is added to the graph edge (updatePheromone()
function, Algorithm.java
file)
ants.forEach((ant) -> {
// pheromoneReserve - pheromone amount for each ant
// the longer the path the less we add
double pheromoneInc = pheromoneReserve / ant.getChainLength();
for (int i = 0; i < numOfCities; i++) {
// we add popularity to every edge ant went through
getWaysPopularity()[ant.citiesChain[i]][ant.citiesChain[(i + 1) % numOfCities]]++;
// we add pheromone to every edge ant went through
getWaysPheromone()[ant.citiesChain[i]][ant.citiesChain[(i + 1) % numOfCities]] += pheromoneInc;
}
});
Algorithm.java
file)
private static void updateBest() {
//
if (bestChain == null) {
bestChainLength = ants.get(0).getChainLength();
bestChain = ants.get(0).getCitiesChain();
}
// we search for the shortest way and the chain of the city it produced
ants.forEach((ant) -> {
if (ant.getChainLength() < bestChainLength) {
bestChainLength = ant.getChainLength();
bestChain = ant.getCitiesChain().clone();
}
});
}