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 * <https://shark-ml.github.io/Shark/>
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
64using namespace shark;
65typedef IntVector Tour;
66
67/**
68 * \brief Defines the problem, i.e., its cost matrix.
69 */
70const 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
83typedef 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
90typedef boost::graph_traits<Graph>::vertex_descriptor Vertex;
91typedef boost::graph_traits<Graph>::edge_descriptor Edge;
92typedef boost::property_map<Graph, boost::edge_weight_t>::type WeightMap;
93typedef boost::property_map<Graph, boost::edge_color_t>::type ColorMap;
94
96typedef std::vector< IndividualType > Population;
97
98
99/// \brief Calculates the cost of a tour w.r.t. to a cost matrix.
100struct 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() );
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
128int 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}