View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.math4.examples.sofm.tsp;
19  
20  import java.io.FileNotFoundException;
21  import java.io.PrintWriter;
22  import java.io.UnsupportedEncodingException;
23  import java.nio.charset.StandardCharsets;
24  import java.util.concurrent.Callable;
25  
26  import picocli.CommandLine;
27  import picocli.CommandLine.Option;
28  import picocli.CommandLine.Command;
29  
30  import org.apache.commons.rng.UniformRandomProvider;
31  import org.apache.commons.rng.simple.RandomSource;
32  
33  /**
34   * Application class.
35   */
36  @Command(description = "Run the application",
37           mixinStandardHelpOptions = true)
38  public final class StandAlone implements Callable<Void> {
39      /** The neurons per city. */
40      @Option(names = { "-n" }, paramLabel = "neuronsPerCity",
41              description = "Average number of neurons per city (default: ${DEFAULT-VALUE}).")
42      private double neuronsPerCity = 2.2;
43      /** The number of samples. */
44      @Option(names = { "-s" }, paramLabel = "numSamples",
45              description = "Number of samples for the training (default: ${DEFAULT-VALUE}).")
46      private long numSamples = 2000L;
47      /** The number of jobs. */
48      @Option(names = { "-j" }, paramLabel = "numJobs",
49              description = "Number of concurrent tasks (default: ${DEFAULT-VALUE}).")
50      private int numJobs = Runtime.getRuntime().availableProcessors();
51      /** The maximum number of trials. */
52      @Option(names = { "-m" }, paramLabel = "maxTrials",
53              description = "Maximal number of trials (default: ${DEFAULT-VALUE}).")
54      private int maxTrials = 10;
55      /** The output file. */
56      @Option(names = { "-o" }, paramLabel = "outputFile", required = true,
57              description = "Output file name.")
58      private String outputFile;
59  
60      /**
61       * Program entry point.
62       *
63       * @param args Command line arguments and options.
64       */
65      public static void main(String[] args) {
66          CommandLine.call(new StandAlone(), args);
67      }
68  
69      @Override
70      public Void call() throws FileNotFoundException, UnsupportedEncodingException {
71          // Cities (in optimal travel order).
72          final City[] cities = {
73              new City("o0", 0, 0),
74              new City("o1", 1, 0),
75              new City("o2", 2, 0),
76              new City("o3", 3, 0),
77              new City("o4", 3, 1),
78              new City("o5", 3, 2),
79              new City("o6", 3, 3),
80              new City("o7", 2, 3),
81              new City("o8", 1, 3),
82              new City("o9", 0, 3),
83              new City("i3", 1, 2),
84              new City("i2", 2, 2),
85              new City("i1", 2, 1),
86              new City("i0", 1, 1),
87          };
88  
89          final UniformRandomProvider rng = RandomSource.KISS.create();
90          City[] best = null;
91          int maxCities = 0;
92          double minDist = Double.POSITIVE_INFINITY;
93  
94          int count = 0;
95          while (count++ < maxTrials) {
96              final City[] travel = TravellingSalesmanSolver.solve(cities,
97                                                                   neuronsPerCity,
98                                                                   numSamples,
99                                                                   numJobs,
100                                                                  rng);
101             final int numCities = City.unique(travel).size();
102             if (numCities > maxCities) {
103                 best = travel;
104                 maxCities = numCities;
105             }
106 
107             if (numCities == cities.length) {
108                 final double dist = computeDistance(travel);
109                 if (dist < minDist) {
110                     minDist = dist;
111                     best = travel;
112                 }
113             }
114         }
115 
116         printSummary(outputFile, best, computeDistance(cities));
117 
118         return null;
119     }
120     /**
121      * Compute the distance covered by the salesman, including
122      * the trip back (from the last to first city).
123      *
124      * @param cityList List of cities visited during the travel.
125      * @return the total distance.
126      */
127     private static double computeDistance(City[] cityList) {
128         double dist = 0;
129         for (int i = 0; i < cityList.length; i++) {
130             final double[] currentCoord = cityList[i].getCoordinates();
131             final double[] nextCoord = cityList[(i + 1) % cityList.length].getCoordinates();
132 
133             final double xDiff = currentCoord[0] - nextCoord[0];
134             final double yDiff = currentCoord[1] - nextCoord[1];
135 
136             dist += Math.sqrt(xDiff * xDiff + yDiff * yDiff);
137         }
138 
139         return dist;
140     }
141 
142     /**
143      * Prints a summary of the current state of the solver to the
144      * given file name.
145      *
146      * @param fileName File.
147      * @param travel Solution.
148      * @param optimalDistance Length of shortest path.
149      * @throws UnsupportedEncodingException If UTF-8 encoding does not exist.
150      * @throws FileNotFoundException If the file cannot be created.
151      */
152     private static void printSummary(String fileName,
153                                      City[] travel,
154                                      double optimalDistance)
155                                      throws FileNotFoundException, UnsupportedEncodingException {
156         try (PrintWriter out = new PrintWriter(fileName, StandardCharsets.UTF_8.name())) {
157             out.println("# Number of unique cities: " + City.unique(travel).size());
158             out.println("# Travel distance: " + computeDistance(travel));
159             out.println("# Optimal travel distance: " + optimalDistance);
160 
161             for (final City c : travel) {
162                 final double[] coord = c.getCoordinates();
163                 out.println(coord[0] + " " + coord[1] + " # " + c.getName());
164             }
165         }
166     }
167 }