metric_tsp_approx.hpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. //=======================================================================
  2. // Copyright 2008
  3. // Author: Matyas W Egyhazy
  4. //
  5. // Distributed under the Boost Software License, Version 1.0. (See
  6. // accompanying file LICENSE_1_0.txt or copy at
  7. // http://www.boost.org/LICENSE_1_0.txt)
  8. //=======================================================================
  9. #ifndef BOOST_GRAPH_METRIC_TSP_APPROX_HPP
  10. #define BOOST_GRAPH_METRIC_TSP_APPROX_HPP
  11. // metric_tsp_approx
  12. // Generates an approximate tour solution for the traveling salesperson
  13. // problem in polynomial time. The current algorithm guarantees a tour with a
  14. // length at most as long as 2x optimal solution. The graph should have
  15. // 'natural' (metric) weights such that the triangle inequality is maintained.
  16. // Graphs must be fully interconnected.
  17. // TODO:
  18. // There are a couple of improvements that could be made.
  19. // 1) Change implementation to lower uppper bound Christofides heuristic
  20. // 2) Implement a less restrictive TSP heuristic (one that does not rely on
  21. // triangle inequality).
  22. // 3) Determine if the algorithm can be implemented without creating a new
  23. // graph.
  24. #include <vector>
  25. #include <boost/shared_ptr.hpp>
  26. #include <boost/concept_check.hpp>
  27. #include <boost/graph/graph_traits.hpp>
  28. #include <boost/graph/graph_as_tree.hpp>
  29. #include <boost/graph/adjacency_list.hpp>
  30. #include <boost/graph/prim_minimum_spanning_tree.hpp>
  31. #include <boost/graph/lookup_edge.hpp>
  32. #include <boost/throw_exception.hpp>
  33. namespace boost
  34. {
  35. // Define a concept for the concept-checking library.
  36. template <typename Visitor, typename Graph>
  37. struct TSPVertexVisitorConcept
  38. {
  39. private:
  40. Visitor vis_;
  41. public:
  42. typedef typename graph_traits<Graph>::vertex_descriptor Vertex;
  43. BOOST_CONCEPT_USAGE(TSPVertexVisitorConcept)
  44. {
  45. Visitor vis(vis_); // require copy construction
  46. Graph g(1);
  47. Vertex v(*vertices(g).first);
  48. vis.visit_vertex(v, g); // require visit_vertex
  49. }
  50. };
  51. // Tree visitor that keeps track of a preorder traversal of a tree
  52. // TODO: Consider migrating this to the graph_as_tree header.
  53. // TODO: Parameterize the underlying stores so it doesn't have to be a vector.
  54. template<typename Node, typename Tree> class PreorderTraverser
  55. {
  56. private:
  57. std::vector<Node>& path_;
  58. public:
  59. typedef typename std::vector<Node>::const_iterator const_iterator;
  60. PreorderTraverser(std::vector<Node>& p) : path_(p) {}
  61. void preorder(Node n, const Tree&)
  62. { path_.push_back(n); }
  63. void inorder(Node, const Tree&) const {}
  64. void postorder(Node, const Tree&) const {}
  65. const_iterator begin() const { return path_.begin(); }
  66. const_iterator end() const { return path_.end(); }
  67. };
  68. // Forward declarations
  69. template <typename> class tsp_tour_visitor;
  70. template <typename, typename, typename, typename> class tsp_tour_len_visitor;
  71. template<typename VertexListGraph, typename OutputIterator>
  72. void metric_tsp_approx_tour(VertexListGraph& g, OutputIterator o)
  73. {
  74. metric_tsp_approx_from_vertex(g, *vertices(g).first,
  75. get(edge_weight, g), get(vertex_index, g),
  76. tsp_tour_visitor<OutputIterator>(o));
  77. }
  78. template<typename VertexListGraph, typename WeightMap, typename OutputIterator>
  79. void metric_tsp_approx_tour(VertexListGraph& g, WeightMap w, OutputIterator o)
  80. {
  81. metric_tsp_approx_from_vertex(g, *vertices(g).first,
  82. w, tsp_tour_visitor<OutputIterator>(o));
  83. }
  84. template<typename VertexListGraph, typename OutputIterator>
  85. void metric_tsp_approx_tour_from_vertex(VertexListGraph& g,
  86. typename graph_traits<VertexListGraph>::vertex_descriptor start,
  87. OutputIterator o)
  88. {
  89. metric_tsp_approx_from_vertex(g, start, get(edge_weight, g),
  90. get(vertex_index, g), tsp_tour_visitor<OutputIterator>(o));
  91. }
  92. template<typename VertexListGraph, typename WeightMap,
  93. typename OutputIterator>
  94. void metric_tsp_approx_tour_from_vertex(VertexListGraph& g,
  95. typename graph_traits<VertexListGraph>::vertex_descriptor start,
  96. WeightMap w, OutputIterator o)
  97. {
  98. metric_tsp_approx_from_vertex(g, start, w, get(vertex_index, g),
  99. tsp_tour_visitor<OutputIterator>(o));
  100. }
  101. template<typename VertexListGraph, typename TSPVertexVisitor>
  102. void metric_tsp_approx(VertexListGraph& g, TSPVertexVisitor vis)
  103. {
  104. metric_tsp_approx_from_vertex(g, *vertices(g).first,
  105. get(edge_weight, g), get(vertex_index, g), vis);
  106. }
  107. template<typename VertexListGraph, typename Weightmap,
  108. typename VertexIndexMap, typename TSPVertexVisitor>
  109. void metric_tsp_approx(VertexListGraph& g, Weightmap w,
  110. TSPVertexVisitor vis)
  111. {
  112. metric_tsp_approx_from_vertex(g, *vertices(g).first, w,
  113. get(vertex_index, g), vis);
  114. }
  115. template<typename VertexListGraph, typename WeightMap,
  116. typename VertexIndexMap, typename TSPVertexVisitor>
  117. void metric_tsp_approx(VertexListGraph& g, WeightMap w, VertexIndexMap id,
  118. TSPVertexVisitor vis)
  119. {
  120. metric_tsp_approx_from_vertex(g, *vertices(g).first, w, id, vis);
  121. }
  122. template<typename VertexListGraph, typename WeightMap,
  123. typename TSPVertexVisitor>
  124. void metric_tsp_approx_from_vertex(VertexListGraph& g,
  125. typename graph_traits<VertexListGraph>::vertex_descriptor start,
  126. WeightMap w, TSPVertexVisitor vis)
  127. {
  128. metric_tsp_approx_from_vertex(g, start, w, get(vertex_index, g), vis);
  129. }
  130. template <
  131. typename VertexListGraph,
  132. typename WeightMap,
  133. typename VertexIndexMap,
  134. typename TSPVertexVisitor>
  135. void metric_tsp_approx_from_vertex(const VertexListGraph& g,
  136. typename graph_traits<VertexListGraph>::vertex_descriptor start,
  137. WeightMap weightmap,
  138. VertexIndexMap indexmap,
  139. TSPVertexVisitor vis)
  140. {
  141. using namespace boost;
  142. using namespace std;
  143. BOOST_CONCEPT_ASSERT((VertexListGraphConcept<VertexListGraph>));
  144. BOOST_CONCEPT_ASSERT((TSPVertexVisitorConcept<TSPVertexVisitor, VertexListGraph>));
  145. // Types related to the input graph (GVertex is a template parameter).
  146. typedef typename graph_traits<VertexListGraph>::vertex_descriptor GVertex;
  147. typedef typename graph_traits<VertexListGraph>::vertex_iterator GVItr;
  148. // We build a custom graph in this algorithm.
  149. typedef adjacency_list <vecS, vecS, directedS, no_property, no_property > MSTImpl;
  150. typedef graph_traits<MSTImpl>::vertex_descriptor Vertex;
  151. typedef graph_traits<MSTImpl>::vertex_iterator VItr;
  152. // And then re-cast it as a tree.
  153. typedef iterator_property_map<vector<Vertex>::iterator, property_map<MSTImpl, vertex_index_t>::type> ParentMap;
  154. typedef graph_as_tree<MSTImpl, ParentMap> Tree;
  155. typedef tree_traits<Tree>::node_descriptor Node;
  156. // A predecessor map.
  157. typedef vector<GVertex> PredMap;
  158. typedef iterator_property_map<typename PredMap::iterator, VertexIndexMap> PredPMap;
  159. PredMap preds(num_vertices(g));
  160. PredPMap pred_pmap(preds.begin(), indexmap);
  161. // Compute a spanning tree over the in put g.
  162. prim_minimum_spanning_tree(g, pred_pmap,
  163. root_vertex(start)
  164. .vertex_index_map(indexmap)
  165. .weight_map(weightmap));
  166. // Build a MST using the predecessor map from prim mst
  167. MSTImpl mst(num_vertices(g));
  168. std::size_t cnt = 0;
  169. pair<VItr, VItr> mst_verts(vertices(mst));
  170. for(typename PredMap::iterator vi(preds.begin()); vi != preds.end(); ++vi, ++cnt)
  171. {
  172. if(indexmap[*vi] != cnt) {
  173. add_edge(*next(mst_verts.first, indexmap[*vi]),
  174. *next(mst_verts.first, cnt), mst);
  175. }
  176. }
  177. // Build a tree abstraction over the MST.
  178. vector<Vertex> parent(num_vertices(mst));
  179. Tree t(mst, *vertices(mst).first,
  180. make_iterator_property_map(parent.begin(),
  181. get(vertex_index, mst)));
  182. // Create tour using a preorder traversal of the mst
  183. vector<Node> tour;
  184. PreorderTraverser<Node, Tree> tvis(tour);
  185. traverse_tree(indexmap[start], t, tvis);
  186. pair<GVItr, GVItr> g_verts(vertices(g));
  187. for(PreorderTraverser<Node, Tree>::const_iterator curr(tvis.begin());
  188. curr != tvis.end(); ++curr)
  189. {
  190. // TODO: This is will be O(n^2) if vertex storage of g != vecS.
  191. GVertex v = *next(g_verts.first, get(vertex_index, mst)[*curr]);
  192. vis.visit_vertex(v, g);
  193. }
  194. // Connect back to the start of the tour
  195. vis.visit_vertex(start, g);
  196. }
  197. // Default tsp tour visitor that puts the tour in an OutputIterator
  198. template <typename OutItr>
  199. class tsp_tour_visitor
  200. {
  201. OutItr itr_;
  202. public:
  203. tsp_tour_visitor(OutItr itr)
  204. : itr_(itr)
  205. { }
  206. template <typename Vertex, typename Graph>
  207. void visit_vertex(Vertex v, const Graph&)
  208. {
  209. BOOST_CONCEPT_ASSERT((OutputIterator<OutItr, Vertex>));
  210. *itr_++ = v;
  211. }
  212. };
  213. // Tsp tour visitor that adds the total tour length.
  214. template<typename Graph, typename WeightMap, typename OutIter, typename Length>
  215. class tsp_tour_len_visitor
  216. {
  217. typedef typename graph_traits<Graph>::vertex_descriptor Vertex;
  218. BOOST_CONCEPT_ASSERT((OutputIterator<OutIter, Vertex>));
  219. OutIter iter_;
  220. Length& tourlen_;
  221. WeightMap& wmap_;
  222. Vertex previous_;
  223. // Helper function for getting the null vertex.
  224. Vertex null()
  225. { return graph_traits<Graph>::null_vertex(); }
  226. public:
  227. tsp_tour_len_visitor(Graph const&, OutIter iter, Length& l, WeightMap& map)
  228. : iter_(iter), tourlen_(l), wmap_(map), previous_(null())
  229. { }
  230. void visit_vertex(Vertex v, const Graph& g)
  231. {
  232. typedef typename graph_traits<Graph>::edge_descriptor Edge;
  233. // If it is not the start, then there is a
  234. // previous vertex
  235. if(previous_ != null())
  236. {
  237. // NOTE: For non-adjacency matrix graphs g, this bit of code
  238. // will be linear in the degree of previous_ or v. A better
  239. // solution would be to visit edges of the graph, but that
  240. // would require revisiting the core algorithm.
  241. Edge e;
  242. bool found;
  243. boost::tie(e, found) = lookup_edge(previous_, v, g);
  244. if(!found) {
  245. BOOST_THROW_EXCEPTION(not_complete());
  246. }
  247. tourlen_ += wmap_[e];
  248. }
  249. previous_ = v;
  250. *iter_++ = v;
  251. }
  252. };
  253. // Object generator(s)
  254. template <typename OutIter>
  255. inline tsp_tour_visitor<OutIter>
  256. make_tsp_tour_visitor(OutIter iter)
  257. { return tsp_tour_visitor<OutIter>(iter); }
  258. template <typename Graph, typename WeightMap, typename OutIter, typename Length>
  259. inline tsp_tour_len_visitor<Graph, WeightMap, OutIter, Length>
  260. make_tsp_tour_len_visitor(Graph const& g, OutIter iter, Length& l, WeightMap map)
  261. { return tsp_tour_len_visitor<Graph, WeightMap, OutIter, Length>(g, iter, l, map); }
  262. } //boost
  263. #endif // BOOST_GRAPH_METRIC_TSP_APPROX_HPP