Chris@16
|
1 //=======================================================================
|
Chris@16
|
2 // Copyright 2007 Aaron Windsor
|
Chris@16
|
3 //
|
Chris@16
|
4 // Distributed under the Boost Software License, Version 1.0. (See
|
Chris@16
|
5 // accompanying file LICENSE_1_0.txt or copy at
|
Chris@16
|
6 // http://www.boost.org/LICENSE_1_0.txt)
|
Chris@16
|
7 //=======================================================================
|
Chris@16
|
8 #ifndef __IS_KURATOWSKI_SUBGRAPH_HPP__
|
Chris@16
|
9 #define __IS_KURATOWSKI_SUBGRAPH_HPP__
|
Chris@16
|
10
|
Chris@16
|
11 #include <boost/config.hpp>
|
Chris@16
|
12 #include <boost/tuple/tuple.hpp> //for tie
|
Chris@16
|
13 #include <boost/property_map/property_map.hpp>
|
Chris@16
|
14 #include <boost/graph/properties.hpp>
|
Chris@16
|
15 #include <boost/graph/isomorphism.hpp>
|
Chris@16
|
16 #include <boost/graph/adjacency_list.hpp>
|
Chris@16
|
17
|
Chris@16
|
18 #include <algorithm>
|
Chris@16
|
19 #include <vector>
|
Chris@16
|
20 #include <set>
|
Chris@16
|
21
|
Chris@16
|
22
|
Chris@16
|
23
|
Chris@16
|
24 namespace boost
|
Chris@16
|
25 {
|
Chris@16
|
26
|
Chris@16
|
27 namespace detail
|
Chris@16
|
28 {
|
Chris@16
|
29
|
Chris@16
|
30 template <typename Graph>
|
Chris@16
|
31 Graph make_K_5()
|
Chris@16
|
32 {
|
Chris@16
|
33 typename graph_traits<Graph>::vertex_iterator vi, vi_end, inner_vi;
|
Chris@16
|
34 Graph K_5(5);
|
Chris@16
|
35 for(boost::tie(vi,vi_end) = vertices(K_5); vi != vi_end; ++vi)
|
Chris@16
|
36 for(inner_vi = next(vi); inner_vi != vi_end; ++inner_vi)
|
Chris@16
|
37 add_edge(*vi, *inner_vi, K_5);
|
Chris@16
|
38 return K_5;
|
Chris@16
|
39 }
|
Chris@16
|
40
|
Chris@16
|
41
|
Chris@16
|
42 template <typename Graph>
|
Chris@16
|
43 Graph make_K_3_3()
|
Chris@16
|
44 {
|
Chris@16
|
45 typename graph_traits<Graph>::vertex_iterator
|
Chris@16
|
46 vi, vi_end, bipartition_start, inner_vi;
|
Chris@16
|
47 Graph K_3_3(6);
|
Chris@16
|
48 bipartition_start = next(next(next(vertices(K_3_3).first)));
|
Chris@16
|
49 for(boost::tie(vi, vi_end) = vertices(K_3_3); vi != bipartition_start; ++vi)
|
Chris@16
|
50 for(inner_vi= bipartition_start; inner_vi != vi_end; ++inner_vi)
|
Chris@16
|
51 add_edge(*vi, *inner_vi, K_3_3);
|
Chris@16
|
52 return K_3_3;
|
Chris@16
|
53 }
|
Chris@16
|
54
|
Chris@16
|
55
|
Chris@16
|
56 template <typename AdjacencyList, typename Vertex>
|
Chris@16
|
57 void contract_edge(AdjacencyList& neighbors, Vertex u, Vertex v)
|
Chris@16
|
58 {
|
Chris@16
|
59 // Remove u from v's neighbor list
|
Chris@16
|
60 neighbors[v].erase(std::remove(neighbors[v].begin(),
|
Chris@16
|
61 neighbors[v].end(), u
|
Chris@16
|
62 ),
|
Chris@16
|
63 neighbors[v].end()
|
Chris@16
|
64 );
|
Chris@16
|
65
|
Chris@16
|
66 // Replace any references to u with references to v
|
Chris@16
|
67 typedef typename AdjacencyList::value_type::iterator
|
Chris@16
|
68 adjacency_iterator_t;
|
Chris@16
|
69
|
Chris@16
|
70 adjacency_iterator_t u_neighbor_end = neighbors[u].end();
|
Chris@16
|
71 for(adjacency_iterator_t u_neighbor_itr = neighbors[u].begin();
|
Chris@16
|
72 u_neighbor_itr != u_neighbor_end; ++u_neighbor_itr
|
Chris@16
|
73 )
|
Chris@16
|
74 {
|
Chris@16
|
75 Vertex u_neighbor(*u_neighbor_itr);
|
Chris@16
|
76 std::replace(neighbors[u_neighbor].begin(),
|
Chris@16
|
77 neighbors[u_neighbor].end(), u, v
|
Chris@16
|
78 );
|
Chris@16
|
79 }
|
Chris@16
|
80
|
Chris@16
|
81 // Remove v from u's neighbor list
|
Chris@16
|
82 neighbors[u].erase(std::remove(neighbors[u].begin(),
|
Chris@16
|
83 neighbors[u].end(), v
|
Chris@16
|
84 ),
|
Chris@16
|
85 neighbors[u].end()
|
Chris@16
|
86 );
|
Chris@16
|
87
|
Chris@16
|
88 // Add everything in u's neighbor list to v's neighbor list
|
Chris@16
|
89 std::copy(neighbors[u].begin(),
|
Chris@16
|
90 neighbors[u].end(),
|
Chris@16
|
91 std::back_inserter(neighbors[v])
|
Chris@16
|
92 );
|
Chris@16
|
93
|
Chris@16
|
94 // Clear u's neighbor list
|
Chris@16
|
95 neighbors[u].clear();
|
Chris@16
|
96
|
Chris@16
|
97 }
|
Chris@16
|
98
|
Chris@16
|
99 enum target_graph_t { tg_k_3_3, tg_k_5};
|
Chris@16
|
100
|
Chris@16
|
101 } // namespace detail
|
Chris@16
|
102
|
Chris@16
|
103
|
Chris@16
|
104
|
Chris@16
|
105
|
Chris@16
|
106 template <typename Graph, typename ForwardIterator, typename VertexIndexMap>
|
Chris@16
|
107 bool is_kuratowski_subgraph(const Graph& g,
|
Chris@16
|
108 ForwardIterator begin,
|
Chris@16
|
109 ForwardIterator end,
|
Chris@16
|
110 VertexIndexMap vm
|
Chris@16
|
111 )
|
Chris@16
|
112 {
|
Chris@16
|
113
|
Chris@16
|
114 typedef typename graph_traits<Graph>::vertex_descriptor vertex_t;
|
Chris@16
|
115 typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator_t;
|
Chris@16
|
116 typedef typename graph_traits<Graph>::edge_descriptor edge_t;
|
Chris@16
|
117 typedef typename graph_traits<Graph>::edges_size_type e_size_t;
|
Chris@16
|
118 typedef typename graph_traits<Graph>::vertices_size_type v_size_t;
|
Chris@16
|
119 typedef typename std::vector<vertex_t> v_list_t;
|
Chris@16
|
120 typedef typename v_list_t::iterator v_list_iterator_t;
|
Chris@16
|
121 typedef iterator_property_map
|
Chris@16
|
122 <typename std::vector<v_list_t>::iterator, VertexIndexMap>
|
Chris@16
|
123 vertex_to_v_list_map_t;
|
Chris@16
|
124
|
Chris@16
|
125 typedef adjacency_list<vecS, vecS, undirectedS> small_graph_t;
|
Chris@16
|
126
|
Chris@16
|
127 detail::target_graph_t target_graph = detail::tg_k_3_3; //unless we decide otherwise later
|
Chris@16
|
128
|
Chris@16
|
129 static small_graph_t K_5(detail::make_K_5<small_graph_t>());
|
Chris@16
|
130
|
Chris@16
|
131 static small_graph_t K_3_3(detail::make_K_3_3<small_graph_t>());
|
Chris@16
|
132
|
Chris@16
|
133 v_size_t n_vertices(num_vertices(g));
|
Chris@16
|
134 v_size_t max_num_edges(3*n_vertices - 5);
|
Chris@16
|
135
|
Chris@16
|
136 std::vector<v_list_t> neighbors_vector(n_vertices);
|
Chris@16
|
137 vertex_to_v_list_map_t neighbors(neighbors_vector.begin(), vm);
|
Chris@16
|
138
|
Chris@16
|
139 e_size_t count = 0;
|
Chris@16
|
140 for(ForwardIterator itr = begin; itr != end; ++itr)
|
Chris@16
|
141 {
|
Chris@16
|
142
|
Chris@16
|
143 if (count++ > max_num_edges)
|
Chris@16
|
144 return false;
|
Chris@16
|
145
|
Chris@16
|
146 edge_t e(*itr);
|
Chris@16
|
147 vertex_t u(source(e,g));
|
Chris@16
|
148 vertex_t v(target(e,g));
|
Chris@16
|
149
|
Chris@16
|
150 neighbors[u].push_back(v);
|
Chris@16
|
151 neighbors[v].push_back(u);
|
Chris@16
|
152
|
Chris@16
|
153 }
|
Chris@16
|
154
|
Chris@16
|
155
|
Chris@16
|
156 for(v_size_t max_size = 2; max_size < 5; ++max_size)
|
Chris@16
|
157 {
|
Chris@16
|
158
|
Chris@16
|
159 vertex_iterator_t vi, vi_end;
|
Chris@16
|
160 for(boost::tie(vi,vi_end) = vertices(g); vi != vi_end; ++vi)
|
Chris@16
|
161 {
|
Chris@16
|
162 vertex_t v(*vi);
|
Chris@16
|
163
|
Chris@16
|
164 //a hack to make sure we don't contract the middle edge of a path
|
Chris@16
|
165 //of four degree-3 vertices
|
Chris@16
|
166 if (max_size == 4 && neighbors[v].size() == 3)
|
Chris@16
|
167 {
|
Chris@16
|
168 if (neighbors[neighbors[v][0]].size() +
|
Chris@16
|
169 neighbors[neighbors[v][1]].size() +
|
Chris@16
|
170 neighbors[neighbors[v][2]].size()
|
Chris@16
|
171 < 11 // so, it has two degree-3 neighbors
|
Chris@16
|
172 )
|
Chris@16
|
173 continue;
|
Chris@16
|
174 }
|
Chris@16
|
175
|
Chris@16
|
176 while (neighbors[v].size() > 0 && neighbors[v].size() < max_size)
|
Chris@16
|
177 {
|
Chris@16
|
178 // Find one of v's neighbors u such that v and u
|
Chris@16
|
179 // have no neighbors in common. We'll look for such a
|
Chris@16
|
180 // neighbor with a naive cubic-time algorithm since the
|
Chris@16
|
181 // max size of any of the neighbor sets we'll consider
|
Chris@16
|
182 // merging is 3
|
Chris@16
|
183
|
Chris@16
|
184 bool neighbor_sets_intersect = false;
|
Chris@16
|
185
|
Chris@16
|
186 vertex_t min_u = graph_traits<Graph>::null_vertex();
|
Chris@16
|
187 vertex_t u;
|
Chris@16
|
188 v_list_iterator_t v_neighbor_end = neighbors[v].end();
|
Chris@16
|
189 for(v_list_iterator_t v_neighbor_itr = neighbors[v].begin();
|
Chris@16
|
190 v_neighbor_itr != v_neighbor_end;
|
Chris@16
|
191 ++v_neighbor_itr
|
Chris@16
|
192 )
|
Chris@16
|
193 {
|
Chris@16
|
194 neighbor_sets_intersect = false;
|
Chris@16
|
195 u = *v_neighbor_itr;
|
Chris@16
|
196 v_list_iterator_t u_neighbor_end = neighbors[u].end();
|
Chris@16
|
197 for(v_list_iterator_t u_neighbor_itr =
|
Chris@16
|
198 neighbors[u].begin();
|
Chris@16
|
199 u_neighbor_itr != u_neighbor_end &&
|
Chris@16
|
200 !neighbor_sets_intersect;
|
Chris@16
|
201 ++u_neighbor_itr
|
Chris@16
|
202 )
|
Chris@16
|
203 {
|
Chris@16
|
204 for(v_list_iterator_t inner_v_neighbor_itr =
|
Chris@16
|
205 neighbors[v].begin();
|
Chris@16
|
206 inner_v_neighbor_itr != v_neighbor_end;
|
Chris@16
|
207 ++inner_v_neighbor_itr
|
Chris@16
|
208 )
|
Chris@16
|
209 {
|
Chris@16
|
210 if (*u_neighbor_itr == *inner_v_neighbor_itr)
|
Chris@16
|
211 {
|
Chris@16
|
212 neighbor_sets_intersect = true;
|
Chris@16
|
213 break;
|
Chris@16
|
214 }
|
Chris@16
|
215 }
|
Chris@16
|
216
|
Chris@16
|
217 }
|
Chris@16
|
218 if (!neighbor_sets_intersect &&
|
Chris@16
|
219 (min_u == graph_traits<Graph>::null_vertex() ||
|
Chris@16
|
220 neighbors[u].size() < neighbors[min_u].size())
|
Chris@16
|
221 )
|
Chris@16
|
222 {
|
Chris@16
|
223 min_u = u;
|
Chris@16
|
224 }
|
Chris@16
|
225
|
Chris@16
|
226 }
|
Chris@16
|
227
|
Chris@16
|
228 if (min_u == graph_traits<Graph>::null_vertex())
|
Chris@16
|
229 // Exited the loop without finding an appropriate neighbor of
|
Chris@16
|
230 // v, so v must be a lost cause. Move on to other vertices.
|
Chris@16
|
231 break;
|
Chris@16
|
232 else
|
Chris@16
|
233 u = min_u;
|
Chris@16
|
234
|
Chris@16
|
235 detail::contract_edge(neighbors, u, v);
|
Chris@16
|
236
|
Chris@16
|
237 }//end iteration over v's neighbors
|
Chris@16
|
238
|
Chris@16
|
239 }//end iteration through vertices v
|
Chris@16
|
240
|
Chris@16
|
241 if (max_size == 3)
|
Chris@16
|
242 {
|
Chris@16
|
243 // check to see whether we should go on to find a K_5
|
Chris@16
|
244 for(boost::tie(vi,vi_end) = vertices(g); vi != vi_end; ++vi)
|
Chris@16
|
245 if (neighbors[*vi].size() == 4)
|
Chris@16
|
246 {
|
Chris@16
|
247 target_graph = detail::tg_k_5;
|
Chris@16
|
248 break;
|
Chris@16
|
249 }
|
Chris@16
|
250
|
Chris@16
|
251 if (target_graph == detail::tg_k_3_3)
|
Chris@16
|
252 break;
|
Chris@16
|
253 }
|
Chris@16
|
254
|
Chris@16
|
255 }//end iteration through max degree 2,3, and 4
|
Chris@16
|
256
|
Chris@16
|
257
|
Chris@16
|
258 //Now, there should only be 5 or 6 vertices with any neighbors. Find them.
|
Chris@16
|
259
|
Chris@16
|
260 v_list_t main_vertices;
|
Chris@16
|
261 vertex_iterator_t vi, vi_end;
|
Chris@16
|
262
|
Chris@16
|
263 for(boost::tie(vi,vi_end) = vertices(g); vi != vi_end; ++vi)
|
Chris@16
|
264 {
|
Chris@16
|
265 if (!neighbors[*vi].empty())
|
Chris@16
|
266 main_vertices.push_back(*vi);
|
Chris@16
|
267 }
|
Chris@16
|
268
|
Chris@16
|
269 // create a graph isomorphic to the contracted graph to test
|
Chris@16
|
270 // against K_5 and K_3_3
|
Chris@16
|
271 small_graph_t contracted_graph(main_vertices.size());
|
Chris@16
|
272 std::map<vertex_t,typename graph_traits<small_graph_t>::vertex_descriptor>
|
Chris@16
|
273 contracted_vertex_map;
|
Chris@16
|
274
|
Chris@16
|
275 typename v_list_t::iterator itr, itr_end;
|
Chris@16
|
276 itr_end = main_vertices.end();
|
Chris@16
|
277 typename graph_traits<small_graph_t>::vertex_iterator
|
Chris@16
|
278 si = vertices(contracted_graph).first;
|
Chris@16
|
279
|
Chris@16
|
280 for(itr = main_vertices.begin(); itr != itr_end; ++itr, ++si)
|
Chris@16
|
281 {
|
Chris@16
|
282 contracted_vertex_map[*itr] = *si;
|
Chris@16
|
283 }
|
Chris@16
|
284
|
Chris@16
|
285 typename v_list_t::iterator jtr, jtr_end;
|
Chris@16
|
286 for(itr = main_vertices.begin(); itr != itr_end; ++itr)
|
Chris@16
|
287 {
|
Chris@16
|
288 jtr_end = neighbors[*itr].end();
|
Chris@16
|
289 for(jtr = neighbors[*itr].begin(); jtr != jtr_end; ++jtr)
|
Chris@16
|
290 {
|
Chris@16
|
291 if (get(vm,*itr) < get(vm,*jtr))
|
Chris@16
|
292 {
|
Chris@16
|
293 add_edge(contracted_vertex_map[*itr],
|
Chris@16
|
294 contracted_vertex_map[*jtr],
|
Chris@16
|
295 contracted_graph
|
Chris@16
|
296 );
|
Chris@16
|
297 }
|
Chris@16
|
298 }
|
Chris@16
|
299 }
|
Chris@16
|
300
|
Chris@16
|
301 if (target_graph == detail::tg_k_5)
|
Chris@16
|
302 {
|
Chris@16
|
303 return boost::isomorphism(K_5,contracted_graph);
|
Chris@16
|
304 }
|
Chris@16
|
305 else //target_graph == tg_k_3_3
|
Chris@16
|
306 {
|
Chris@16
|
307 return boost::isomorphism(K_3_3,contracted_graph);
|
Chris@16
|
308 }
|
Chris@16
|
309
|
Chris@16
|
310
|
Chris@16
|
311 }
|
Chris@16
|
312
|
Chris@16
|
313
|
Chris@16
|
314
|
Chris@16
|
315
|
Chris@16
|
316
|
Chris@16
|
317 template <typename Graph, typename ForwardIterator>
|
Chris@16
|
318 bool is_kuratowski_subgraph(const Graph& g,
|
Chris@16
|
319 ForwardIterator begin,
|
Chris@16
|
320 ForwardIterator end
|
Chris@16
|
321 )
|
Chris@16
|
322 {
|
Chris@16
|
323 return is_kuratowski_subgraph(g, begin, end, get(vertex_index,g));
|
Chris@16
|
324 }
|
Chris@16
|
325
|
Chris@16
|
326
|
Chris@16
|
327
|
Chris@16
|
328
|
Chris@16
|
329 }
|
Chris@16
|
330
|
Chris@16
|
331 #endif //__IS_KURATOWSKI_SUBGRAPH_HPP__
|