diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..18d70f0 --- /dev/null +++ b/.classpath @@ -0,0 +1,6 @@ + + + + + + diff --git a/.project b/.project new file mode 100644 index 0000000..c9b5e0a --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + Dynamic_ACO_Example + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..8000cd6 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/Testberlin52.tsp b/Testberlin52.tsp new file mode 100644 index 0000000..7abb307 --- /dev/null +++ b/Testberlin52.tsp @@ -0,0 +1,20 @@ +NAME: berlin52 +TYPE: TSP +COMMENT: 52 locations in Berlin (Groetschel) +DIMENSION: 52 +EDGE_WEIGHT_TYPE: EUC_2D +NODE_COORD_SECTION +1 565.0 575.0 +2 25.0 185.0 +3 345.0 750.0 +4 945.0 685.0 +5 845.0 655.0 +6 880.0 660.0 +7 25.0 230.0 +8 525.0 1000.0 +9 580.0 1175.0 +10 650.0 1130.0 +11 1605.0 620.0 +12 1220.0 580.0 +EOF + diff --git a/berlin52.tsp b/berlin52.tsp new file mode 100644 index 0000000..6f35791 --- /dev/null +++ b/berlin52.tsp @@ -0,0 +1,60 @@ +NAME: berlin52 +TYPE: TSP +COMMENT: 52 locations in Berlin (Groetschel) +DIMENSION: 52 +EDGE_WEIGHT_TYPE: EUC_2D +NODE_COORD_SECTION +1 565.0 575.0 +2 25.0 185.0 +3 345.0 750.0 +4 945.0 685.0 +5 845.0 655.0 +6 880.0 660.0 +7 25.0 230.0 +8 525.0 1000.0 +9 580.0 1175.0 +10 650.0 1130.0 +11 1605.0 620.0 +12 1220.0 580.0 +13 1465.0 200.0 +14 1530.0 5.0 +15 845.0 680.0 +16 725.0 370.0 +17 145.0 665.0 +18 415.0 635.0 +19 510.0 875.0 +20 560.0 365.0 +21 300.0 465.0 +22 520.0 585.0 +23 480.0 415.0 +24 835.0 625.0 +25 975.0 580.0 +26 1215.0 245.0 +27 1320.0 315.0 +28 1250.0 400.0 +29 660.0 180.0 +30 410.0 250.0 +31 420.0 555.0 +32 575.0 665.0 +33 1150.0 1160.0 +34 700.0 580.0 +35 685.0 595.0 +36 685.0 610.0 +37 770.0 610.0 +38 795.0 645.0 +39 720.0 635.0 +40 760.0 650.0 +41 475.0 960.0 +42 95.0 260.0 +43 875.0 920.0 +44 700.0 500.0 +45 555.0 815.0 +46 830.0 485.0 +47 1170.0 65.0 +48 830.0 610.0 +49 605.0 625.0 +50 595.0 360.0 +51 1340.0 725.0 +52 1740.0 245.0 +EOF + diff --git a/src/ACO.java b/src/ACO.java new file mode 100644 index 0000000..be58cb4 --- /dev/null +++ b/src/ACO.java @@ -0,0 +1,261 @@ +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Random; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; + +public class ACO { + + private static final int MAX_ANTS = 2048 * 20; + private final String TSP_FILE; + private double[][] problem; + private double[][] pheromones; + private Random random; + private ExecutorService service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); + + private LinkedBlockingQueue results; + + public static final double PHEROMONE_PERSISTENCE = 0.3d; // between 0 and 1 + + // greedy + public static final double ALPHA = -0.2d; + // rapid selection + public static final double BETA = 9.6d; + + public static final double Q = 0.0001d; // somewhere between 0 and 1 + + public ACO(String file) { + this.TSP_FILE = file; + this.random = new Random(System.currentTimeMillis()); + this.results = new LinkedBlockingQueue(); + } + + private final double[][] readMatrixFromFile() throws IOException { + + final BufferedReader br = new BufferedReader(new FileReader(new File(TSP_FILE))); + + final ArrayList records = new ArrayList(); + + boolean readAhead = false; + String line; + + // Read the TSP file containind coordinates and populate a List of Record object. + while ((line = br.readLine()) != null) { + + if (line.equals("EOF")) { + break; + } + + if (readAhead) { + String[] split = sweepNumbers(line.trim()); + records.add(new Record(Double.parseDouble(split[1].trim()), Double.parseDouble(split[2].trim()))); + } + + if (line.equals("NODE_COORD_SECTION")) { + readAhead = true; + } + } + + br.close(); + + // Create a matrix N x N; N is the size of the list that was populated. + final double[][] localMatrix = new double[records.size()][records.size()]; + System.out.println(records.size()); + + int rIndex = 0; + // For each record populated in the matrix, + for (Record r : records) { + int hIndex = 0; + for (Record h : records) { + // Calculate the euclidian distance between them. + localMatrix[rIndex][hIndex] = calculateEuclidianDistance(r.x, r.y, h.x, h.y); + hIndex++; + } + rIndex++; + } + // The matrix with all the distances within it's vertices. + return localMatrix; + } + + private final String[] sweepNumbers(String trim) { + String[] arr = new String[3]; + int currentIndex = 0; + for (int i = 0; i < trim.length(); i++) { + final char c = trim.charAt(i); + if ((c) != 32) { + for (int f = i + 1; f < trim.length(); f++) { + final char x = trim.charAt(f); + if ((x) == 32) { + arr[currentIndex] = trim.substring(i, f); + currentIndex++; + break; + } else if (f == trim.length() - 1) { + arr[currentIndex] = trim.substring(i, trim.length()); + break; + } + } + i = i + arr[currentIndex - 1].length(); + } + } + return arr; + } + + private final double calculateEuclidianDistance(double x1, double y1, double x2, double y2) { + // In mathematics, the Euclidean distance or Euclidean metric is the "ordinary" distance between two points in Euclidean space. With this distance, Euclidean space becomes a metric space. The + // associated norm is called the Euclidean norm. Older literature refers to the metric as Pythagorean metric. + // http://en.wikipedia.org/wiki/Euclidean_distance + final double xDiff = x2 - x1; + final double yDiff = y2 - y1; + return Math.abs((Math.sqrt((xDiff * xDiff) + (yDiff * yDiff)))); + } + + private final double[][] initializePheromones() { + double randValue = this.random.nextDouble(); + final double[][] localMatrix = new double[problem.length][problem.length]; + + for (int i = 0; i < localMatrix.length; i++) { + for (int j = 0; j < localMatrix[i].length; j++) { + localMatrix[i][j] = randValue; + } + } + + return localMatrix; + } + + private AntResult bestResult(ArrayList list) { + AntResult ar, bestResult; + double bestDistance = Double.MAX_VALUE; + bestResult = null; + + for (int i = 0; i < list.size(); i++) { + ar = list.get(i); + if (ar.getDistance() < bestDistance) { + bestResult = ar; + bestDistance = ar.getDistance(); + } + } + + return bestResult; + } + + private void printBest(ArrayList list) { + AntResult best; + + best = this.bestResult(list); + + if (best != null) + System.out.printf("%s\n", best); + } + + private void findBestWay() { + + ArrayList partialResult = new ArrayList(ACO.MAX_ANTS); + for (int i = 0; i < ACO.MAX_ANTS; i++) + this.service.execute(new Ant(this, this.random.nextInt(this.problem.length))); + + for (int i = 0, j = 0; i < ACO.MAX_ANTS; i++, j++) { + try { + AntResult ar = this.results.take(); + partialResult.add(ar); + + // Pretty cool + if (j % 10000 == 0){ + this.printBest(partialResult); + } + + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + System.out.printf("----Final Result----\n"); + this.printBest(partialResult); + this.service.shutdown(); + } + + private final double calculatePheromones(double current, double newPheromone) { + final double result = (1 - ACO.PHEROMONE_PERSISTENCE) * current + newPheromone; + return result; + } + + public synchronized void adjustPheromone(int x, int y, double newPheromone) { + final double result = calculatePheromones(this.pheromones[x][y], newPheromone); + if (result >= 0.0) { + this.pheromones[x][y] = result; + } else { + this.pheromones[x][y] = 0; + } + } + + public synchronized double readPheromone(int x, int y) { + return pheromones[x][y]; + } + + public void start() throws IOException { + this.problem = this.readMatrixFromFile(); + this.pheromones = this.initializePheromones(); + this.findBestWay(); + + // Print the matrix with all the distance values. + for (int i = 0; i < problem.length; i++) { + for (int j = 0; j < problem[i].length; j++) { + System.out.printf("%g\t", problem[i][j]); + } + System.out.printf("\n"); + } + + } + + public double[][] getProblem() { + return problem; + } + + public LinkedBlockingQueue getResults() { + return results; + } + +} + +class AntResult { + + private int[] way; + private double distance; + + public AntResult(int[] w, double d) { + this.way = w; + this.distance = d; + } + + public int[] getWay() { + return way; + } + + public void setWay(int[] way) { + this.way = way; + } + + public double getDistance() { + return distance; + } + + public void setDistance(double distance) { + this.distance = distance; + } + + public String toString() { + StringBuffer r = new StringBuffer(""); + + r.append("Distance:" + String.valueOf(distance) + "\nNodes:"); + + for (int i = 0; i < way.length; i++) + r.append(String.valueOf(way[i]) + "\t"); + + return r.toString(); + + } + +} diff --git a/src/Ant.java b/src/Ant.java new file mode 100644 index 0000000..c368d6f --- /dev/null +++ b/src/Ant.java @@ -0,0 +1,113 @@ +import java.util.Random; + +public class Ant implements Runnable { + + private ACO instance; + private boolean[] visited; + private int[] way; + private double distance; + private int start; + private int toVisit; + + public Ant(ACO in, int start) { + this.instance = in; + this.visited = new boolean[in.getProblem().length]; + this.way = new int[in.getProblem().length]; + this.visited[start] = true; + this.distance = 0.0; + this.start = start; + this.toVisit = this.visited.length - 1; + } + + private double invertDouble(double distance) { + if (distance == 0.0) + return 0.0; + else + return 1.0 / distance; + } + + private int getNextNode(int lastNode) { + + double columnSum = 0.0; + int danglingUnvisited = -1; + final double[] weights = new double[this.visited.length]; + + if (this.toVisit <= 0) + return -1; + + for (int i = 0; i < this.visited.length; i++) { + columnSum += Math.pow(this.instance.readPheromone(lastNode, i), + ACO.ALPHA) + * Math.pow(this.invertDouble(this.instance.getProblem()[lastNode][i]), + ACO.BETA); + } + + double sum = 0.0; + + for (int x = 0; x < this.visited.length; x++) { + if (!this.visited[x]) { + weights[x] = calculateProbability(x, lastNode, columnSum); + sum += weights[x]; + danglingUnvisited = x; + } + } + + if (sum == 0.0d) + return danglingUnvisited; + + // weighted indexing stuff + double pSum = 0.0; + for (int i = 0; i < this.visited.length; i++) { + pSum += weights[i] / sum; + weights[i] = pSum; + } + + final double r = new Random(System.currentTimeMillis()).nextDouble(); + + for (int i = 0; i < this.visited.length; i++) { + if (!this.visited[i]) { + if (r <= weights[i]) { + return i; + } + } + } + + return -1; + } + + private double calculateProbability(int row, int column, double sum) { + final double p = Math.pow(instance.readPheromone(column, row), + ACO.ALPHA) + * Math.pow(this.invertDouble(this.instance.getProblem()[column][row]), + ACO.BETA); + return p / sum; + } + + @Override + public void run() { + int i, next, lastNode; + + i = 0; + next = lastNode = start; + + while((next = this.getNextNode(lastNode)) != -1) { + this.way[i++] = lastNode; + this.distance += this.instance.getProblem()[lastNode][next]; + final double phero = (ACO.Q / (this.distance)); + this.instance.adjustPheromone(lastNode, next, phero); + this.visited[next] = true; + lastNode = next; + this.toVisit--; + } + + this.distance += this.instance.getProblem()[lastNode][this.start]; + this.way[i] = lastNode; + + try { + this.instance.getResults().put(new AntResult(this.way, this.distance)); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + +} diff --git a/src/Main.java b/src/Main.java new file mode 100644 index 0000000..d5e65ee --- /dev/null +++ b/src/Main.java @@ -0,0 +1,20 @@ +import java.io.IOException; + + +public class Main { + + /** + * @param args + */ + public static void main(String[] args) { + // TODO Auto-generated method stub + ACO aco = new ACO("berlin52.tsp"); + try { + aco.start(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + +} diff --git a/src/Record.java b/src/Record.java new file mode 100644 index 0000000..e67c709 --- /dev/null +++ b/src/Record.java @@ -0,0 +1,10 @@ +class Record { + double x; + double y; + + public Record(double x, double y) { + super(); + this.x = x; + this.y = y; + } + } \ No newline at end of file