TSP.cpp
Go to the documentation of this file.
1 /*!
2  *
3  *
4  * \brief A 10-city traveling salesman problem.
5 
6  * The implementation follows:
7  * <p>
8  * D. E. Goldberg and R. Lingle, Alleles, loci, and traveling
9  * salesman problem. In <em> Proc. of the International Conference on
10  * Genetic Algorithms and Their Applications</em>, pages 154-159,
11  * Pittsburg, PA, 1985 </p>
12 
13  * The traveling salsman problem is a combinatorial optimization task. A
14  * salesman is supposed to visit $n$ cities. Each travelling connection
15  * is associated with a cost (i.e. the time fot the trip). The problem is
16  * to find the cheapest round-route that visits each city exactly once
17  * and returns to the starting point.
18 
19  *
20  *
21  * \author T. Voss
22  * \date -
23  *
24  *
25  * \par Copyright 1995-2017 Shark Development Team
26  *
27  * <BR><HR>
28  * This file is part of Shark.
29  * <http://shark-ml.org/>
30  *
31  * Shark is free software: you can redistribute it and/or modify
32  * it under the terms of the GNU Lesser General Public License as published
33  * by the Free Software Foundation, either version 3 of the License, or
34  * (at your option) any later version.
35  *
36  * Shark is distributed in the hope that it will be useful,
37  * but WITHOUT ANY WARRANTY; without even the implied warranty of
38  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
39  * GNU Lesser General Public License for more details.
40  *
41  * You should have received a copy of the GNU Lesser General Public License
42  * along with Shark. If not, see <http://www.gnu.org/licenses/>.
43  *
44  */
49 
50 #include <shark/LinAlg/Base.h>
51 
52 #include <boost/format.hpp>
53 #if BOOST_VERSION == 106000
54 #include <boost/type_traits/ice.hpp>//Required because of boost 1.60 bug
55 #endif
56 #include <boost/graph/adjacency_matrix.hpp>
57 #include <boost/graph/adjacency_list.hpp>
58 #include <boost/graph/graphml.hpp>
59 #include <boost/graph/graphviz.hpp>
60 #include <boost/graph/metric_tsp_approx.hpp>
61 #include <boost/property_map/dynamic_property_map.hpp>
62 #include <numeric>//for iota
63 
64 using namespace shark;
65 typedef IntVector Tour;
66 
67 /**
68  * \brief Defines the problem, i.e., its cost matrix.
69  */
70 const double cities[10][10] = {
71  { 0, 28, 57, 72, 81, 85, 80, 113, 89, 80 },
72  { 28, 0, 28, 45, 54, 57, 63, 85, 63, 63 },
73  { 57, 28, 0, 20, 30, 28, 57, 57, 40, 57 },
74  { 72, 45, 20, 0, 10, 20, 72, 45, 20, 45 },
75  { 81, 54, 30, 10, 0, 22, 81, 41, 10, 41 },
76  { 85, 57, 28, 20, 22, 0, 63, 28, 28, 63 },
77  { 80, 63, 57, 72, 81, 63, 0, 80, 89, 113 },
78  { 113, 85, 57, 45, 41, 28, 80, 0, 40, 80 },
79  { 89, 63, 40, 20, 10, 28, 89, 40, 0, 40 },
80  { 80, 63, 57, 45, 41, 63, 113, 80, 40, 0 }
81 };
82 
83 typedef boost::adjacency_matrix< boost::undirectedS,
84  boost::property< boost::vertex_color_t, std::string >,
85  boost::property< boost::edge_weight_t, double,
86  boost::property< boost::edge_color_t, std::string >
87  >,
88  boost::no_property
90 typedef boost::graph_traits<Graph>::vertex_descriptor Vertex;
91 typedef boost::graph_traits<Graph>::edge_descriptor Edge;
92 typedef boost::property_map<Graph, boost::edge_weight_t>::type WeightMap;
93 typedef boost::property_map<Graph, boost::edge_color_t>::type ColorMap;
94 
96 typedef std::vector< IndividualType > Population;
97 
98 
99 /// \brief Calculates the cost of a tour w.r.t. to a cost matrix.
100 struct TspTourLength : public SingleObjectiveFunction{
101 
102  /// \brief Default c'tor, initializes the cost matrix.
103  TspTourLength( RealMatrix const& costMatrix) : m_costMatrix( costMatrix )
104  { }
105 
106  std::string name() const
107  { return "TspTourLength"; }
108 
109  std::size_t numberOfVariables() const
110  { return m_costMatrix.size1(); }
111 
112  /**
113  * \brief Calculates the costs of the supplied tour.
114  */
115  ResultType eval( const SearchPointType & input ) const {
116  SIZE_CHECK( input.size() == m_costMatrix.size1() );
117  m_evaluationCounter++;
118  ResultType result(0);
119  for( std::size_t i = 0; i < input.size() - 1; i++ ) {
120  result += m_costMatrix( input( i ), input( i+1 ) );
121  }
122  return result;
123  }
124 
125  RealMatrix m_costMatrix;
126 };
127 
128 int main( int argc, char ** argv ) {
129 
130  // Define the problem instance
131  Graph g( 10 );
132  // Iterate the graph and assign a label to every vertex
133  boost::graph_traits<Graph>::vertex_iterator v, v_end;
134  for( boost::tie(v,v_end) = boost::vertices(g); v != v_end; ++v )
135  boost::put(boost::vertex_color_t(), g, *v, ( boost::format( "City_%1%" ) % *v ).str() );
136  // Get hold of the weight map and the color map for calculation and visualization purposes.
137  WeightMap weightMap = boost::get( boost::edge_weight, g );
138  ColorMap colorMap = boost::get( boost::edge_color, g );
139 
140  // Iterate the graph and insert distances.
141  Edge e;
142  bool inserted = false;
143 
144  RealMatrix costMatrix( 10, 10 );
145  for( std::size_t i = 0; i < costMatrix.size1(); i++ ) {
146  for( std::size_t j = 0; j < costMatrix.size1(); j++ ) {
147 
148  if( i == j ) continue;
149 
150  costMatrix(i,j) = cities[i][j];
151 
152  // Add the edge
153  boost::tie( e, inserted ) = boost::add_edge( i, j, g );
154  if( inserted ) {
155  // Remember distance between cities i and j.
156  weightMap[ e ] = cities[i][j];
157  // Mark the edge as blue.
158  colorMap[ e ] = "blue";
159  }
160  }
161  }
162 
163  // Mating selection operator
165  // Variation (crossover) operator
167  // Fitness function instance.
168  TspTourLength ttl( costMatrix );
169  ttl.init();
170 
171  // Size of the parent population
172  const std::size_t mu = 100;
173  // Size of the offspring population
174  const std::size_t lambda = 100;
175 
176  // Parent population
177  Population parents( mu );
178  // Offspring population
179  Population offspring( lambda );
180 
181  // Default tour: 0,...,9
182  Tour t( 10 );
183  std::iota( t.begin(),t.end(), 0 );
184 
185  // Initialize parents
186  for( std::size_t i = 0; i < parents.size(); i++ ) {
187  parents[i].searchPoint() = t;
188  // Generate a permutation.
189  std::random_shuffle( parents[ i ].searchPoint().begin(), parents[ i ].searchPoint().end() );
190  // Evaluate the individual.
191  parents[i].penalizedFitness() = parents[i].unpenalizedFitness() = ttl( parents[i].searchPoint() );
192  }
193 
194  // Loop until maximum number of fitness function evaluations is reached.
195  while( ttl.evaluationCounter() < 10000 ) {
196  RealVector selectionProbabilities(parents.size());
197  for(std::size_t i = 0; i != parents.size(); ++i){
198  selectionProbabilities(i) = parents[i].unpenalizedFitness();
199  }
200  selectionProbabilities/= sum(selectionProbabilities);
201  // Crossover candidates.
202  for( std::size_t i = 0; i < offspring.size() - 1; i+=2 ) {
203  // Carry out fitness proportional fitness selection and
204  // perform partially mapped crossover on parent individuals.
205  offspring[ i ] = *rws( random::globalRng, parents.begin(), parents.end(), selectionProbabilities );
206  offspring[ i+1 ] = *rws( random::globalRng, parents.begin(), parents.end(), selectionProbabilities );
207  pmx(random::globalRng, offspring[ i ], offspring[ i+1 ]);
208 
209  // Evaluate offspring individuals.
210  offspring[ i ].penalizedFitness() =
211  offspring[ i ].unpenalizedFitness() = ttl( offspring[ i ].searchPoint() );
212 
213  offspring[ i+1 ].penalizedFitness() =
214  offspring[ i+1 ].unpenalizedFitness() = ttl( offspring[ i+1 ].searchPoint() );
215  }
216 
217  // Swap parent and offspring population.
218  std::swap( parents, offspring );
219  }
220 
221  // Sort in ascending order to find best individual.
222  std::sort( parents.begin(), parents.end(), IndividualType::FitnessOrdering());
223 
224  // Extract the best tour currently known.
225  Tour final = parents.front().searchPoint();
226 
227  // Mark the final tour green in the graph.
228  bool extracted = false;
229  for( std::size_t i = 0; i < final.size() - 1; i++ ) {
230  boost::tie( e, extracted ) = boost::edge( Vertex( final( i ) ), Vertex( final( i + 1 ) ), g );
231 
232  if( extracted )
233  colorMap[ e ] = "green";
234  }
235 
236  // Calculate approximate solution
237  double len = 0.0;
238  std::vector< Vertex > approxTour;
239  boost::metric_tsp_approx( g, boost::make_tsp_tour_len_visitor( g, std::back_inserter( approxTour ) , len, weightMap ) );
240  // Mark the approximation red in the graph.
241  for( std::size_t i = 0; i < approxTour.size() - 1; i++ ) {
242  boost::tie( e, extracted ) = boost::edge( approxTour[ i ], approxTour[ i+1 ], g );
243 
244  if( extracted )
245  colorMap[ e ] = "red";
246  }
247 
248  // Output the graph and the final path
249  std::ofstream outGraphviz( "graph.dot" );
250  boost::dynamic_properties dp;
251  dp.property( "node_id", boost::get( boost::vertex_color, g ) );
252  dp.property( "weight", boost::get( boost::edge_weight, g ) );
253  dp.property( "label", boost::get( boost::edge_weight, g ) );
254  dp.property( "color", boost::get( boost::edge_color, g ) );
255  boost::write_graphviz_dp( outGraphviz, g, dp );
256 
257  // Output best solution and corresponding fitness.
258  std::cout << parents.front().searchPoint() << " -> " << parents.front().unpenalizedFitness() << std::endl;
259  // Output approximate solution and corresponding fitness.
260  std::cout << "Approx: " << len << " vs. GA: " << parents.front().unpenalizedFitness() << std::endl;
261 
262  return EXIT_SUCCESS;
263 }