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