1 //=======================================================================
2 // Copyright 2009 Trustees of Indiana University.
3 // Authors: Michael Hansen
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
10 #include <cmath>
11 #include <iostream>
12 #include <fstream>
13 #include <sstream>
14 #include <vector>
15
16 #include <boost/lexical_cast.hpp>
17 #include <boost/random.hpp>
18 #include <boost/graph/adjacency_list.hpp>
19 #include <boost/graph/filtered_graph.hpp>
20 #include <boost/graph/graphviz.hpp>
21 #include <boost/graph/isomorphism.hpp>
22 #include <boost/graph/iteration_macros.hpp>
23 #include <boost/graph/random.hpp>
24 #include <boost/graph/mcgregor_common_subgraphs.hpp>
25 #include <boost/property_map/shared_array_property_map.hpp>
26 #include <boost/core/lightweight_test.hpp>
27
28 bool was_common_subgraph_found = false, output_graphs = false;
29 std::vector< std::string > simple_subgraph_list;
30
31 // Callback that compares incoming graphs to the supplied common
32 // subgraph.
33 template < typename Graph > struct test_callback
34 {
35
test_callbacktest_callback36 test_callback(
37 Graph& common_subgraph, const Graph& graph1, const Graph& graph2)
38 : m_graph1(graph1), m_graph2(graph2), m_common_subgraph(common_subgraph)
39 {
40 }
41
42 template < typename CorrespondenceMapFirstToSecond,
43 typename CorrespondenceMapSecondToFirst >
operator ()test_callback44 bool operator()(CorrespondenceMapFirstToSecond correspondence_map_1_to_2,
45 CorrespondenceMapSecondToFirst correspondence_map_2_to_1,
46 typename boost::graph_traits< Graph >::vertices_size_type subgraph_size)
47 {
48
49 typedef typename boost::graph_traits< Graph >::vertex_descriptor Vertex;
50 typedef typename boost::graph_traits< Graph >::edge_descriptor Edge;
51 typedef std::pair< Edge, bool > EdgeInfo;
52
53 typedef
54 typename boost::property_map< Graph, boost::vertex_index_t >::type
55 VertexIndexMap;
56 typedef
57 typename boost::property_map< Graph, boost::vertex_name_t >::type
58 VertexNameMap;
59 typedef typename boost::property_map< Graph, boost::edge_name_t >::type
60 EdgeNameMap;
61
62 if (subgraph_size != num_vertices(m_common_subgraph))
63 {
64 return (true);
65 }
66
67 // Fill membership maps for both graphs
68 typedef boost::shared_array_property_map< bool, VertexIndexMap >
69 MembershipMap;
70
71 MembershipMap membership_map1(
72 num_vertices(m_graph1), get(boost::vertex_index, m_graph1));
73
74 MembershipMap membership_map2(
75 num_vertices(m_graph2), get(boost::vertex_index, m_graph2));
76
77 boost::fill_membership_map< Graph >(
78 m_graph1, correspondence_map_1_to_2, membership_map1);
79 boost::fill_membership_map< Graph >(
80 m_graph2, correspondence_map_2_to_1, membership_map2);
81
82 // Generate filtered graphs using membership maps
83 typedef typename boost::membership_filtered_graph_traits< Graph,
84 MembershipMap >::graph_type MembershipFilteredGraph;
85
86 MembershipFilteredGraph subgraph1
87 = boost::make_membership_filtered_graph(m_graph1, membership_map1);
88
89 MembershipFilteredGraph subgraph2
90 = boost::make_membership_filtered_graph(m_graph2, membership_map2);
91
92 VertexIndexMap vindex_map1 = get(boost::vertex_index, subgraph1);
93 VertexIndexMap vindex_map2 = get(boost::vertex_index, subgraph2);
94
95 VertexNameMap vname_map_common
96 = get(boost::vertex_name, m_common_subgraph);
97 VertexNameMap vname_map1 = get(boost::vertex_name, subgraph1);
98 VertexNameMap vname_map2 = get(boost::vertex_name, subgraph2);
99
100 EdgeNameMap ename_map_common = get(boost::edge_name, m_common_subgraph);
101 EdgeNameMap ename_map1 = get(boost::edge_name, subgraph1);
102 EdgeNameMap ename_map2 = get(boost::edge_name, subgraph2);
103
104 // Verify that subgraph1 matches the supplied common subgraph
105 BGL_FORALL_VERTICES_T(vertex1, subgraph1, MembershipFilteredGraph)
106 {
107
108 Vertex vertex_common
109 = vertex(get(vindex_map1, vertex1), m_common_subgraph);
110
111 // Match vertex names
112 if (get(vname_map_common, vertex_common)
113 != get(vname_map1, vertex1))
114 {
115
116 // Keep looking
117 return (true);
118 }
119
120 BGL_FORALL_VERTICES_T(vertex1_2, subgraph1, MembershipFilteredGraph)
121 {
122
123 Vertex vertex_common2
124 = vertex(get(vindex_map1, vertex1_2), m_common_subgraph);
125 EdgeInfo edge_common
126 = edge(vertex_common, vertex_common2, m_common_subgraph);
127 EdgeInfo edge1 = edge(vertex1, vertex1_2, subgraph1);
128
129 if ((edge_common.second != edge1.second)
130 || ((edge_common.second && edge1.second)
131 && (get(ename_map_common, edge_common.first)
132 != get(ename_map1, edge1.first))))
133 {
134
135 // Keep looking
136 return (true);
137 }
138 }
139
140 } // BGL_FORALL_VERTICES_T (subgraph1)
141
142 // Verify that subgraph2 matches the supplied common subgraph
143 BGL_FORALL_VERTICES_T(vertex2, subgraph2, MembershipFilteredGraph)
144 {
145
146 Vertex vertex_common
147 = vertex(get(vindex_map2, vertex2), m_common_subgraph);
148
149 // Match vertex names
150 if (get(vname_map_common, vertex_common)
151 != get(vname_map2, vertex2))
152 {
153
154 // Keep looking
155 return (true);
156 }
157
158 BGL_FORALL_VERTICES_T(vertex2_2, subgraph2, MembershipFilteredGraph)
159 {
160
161 Vertex vertex_common2
162 = vertex(get(vindex_map2, vertex2_2), m_common_subgraph);
163 EdgeInfo edge_common
164 = edge(vertex_common, vertex_common2, m_common_subgraph);
165 EdgeInfo edge2 = edge(vertex2, vertex2_2, subgraph2);
166
167 if ((edge_common.second != edge2.second)
168 || ((edge_common.second && edge2.second)
169 && (get(ename_map_common, edge_common.first)
170 != get(ename_map2, edge2.first))))
171 {
172
173 // Keep looking
174 return (true);
175 }
176 }
177
178 } // BGL_FORALL_VERTICES_T (subgraph2)
179
180 // Check isomorphism just to be thorough
181 if (verify_isomorphism(subgraph1, subgraph2, correspondence_map_1_to_2))
182 {
183
184 was_common_subgraph_found = true;
185
186 if (output_graphs)
187 {
188
189 std::fstream file_subgraph(
190 "found_common_subgraph.dot", std::fstream::out);
191 write_graphviz(file_subgraph, subgraph1,
192 make_label_writer(get(boost::vertex_name, m_graph1)),
193 make_label_writer(get(boost::edge_name, m_graph1)));
194 }
195
196 // Stop iterating
197 return (false);
198 }
199
200 // Keep looking
201 return (true);
202 }
203
204 private:
205 const Graph &m_graph1, m_graph2;
206 Graph& m_common_subgraph;
207 };
208
209 template < typename Graph > struct simple_callback
210 {
211
simple_callbacksimple_callback212 simple_callback(const Graph& graph1) : m_graph1(graph1) {}
213
214 template < typename CorrespondenceMapFirstToSecond,
215 typename CorrespondenceMapSecondToFirst >
operator ()simple_callback216 bool operator()(CorrespondenceMapFirstToSecond correspondence_map_1_to_2,
217 CorrespondenceMapSecondToFirst /*correspondence_map_2_to_1*/,
218 typename boost::graph_traits<
219 Graph >::vertices_size_type /*subgraph_size*/)
220 {
221
222 typedef typename boost::graph_traits< Graph >::vertex_descriptor Vertex;
223
224 std::stringstream subgraph_string;
225
226 BGL_FORALL_VERTICES_T(vertex1, m_graph1, Graph)
227 {
228
229 Vertex vertex2 = get(correspondence_map_1_to_2, vertex1);
230
231 if (vertex2 != boost::graph_traits< Graph >::null_vertex())
232 {
233 subgraph_string << vertex1 << "," << vertex2 << " ";
234 }
235 }
236
237 simple_subgraph_list.push_back(subgraph_string.str());
238
239 return (true);
240 }
241
242 private:
243 const Graph& m_graph1;
244 };
245
246 template < typename Graph, typename RandomNumberGenerator,
247 typename VertexNameMap, typename EdgeNameMap >
add_random_vertices(Graph & graph,RandomNumberGenerator & generator,int vertices_to_create,int max_edges_per_vertex,VertexNameMap vname_map,EdgeNameMap ename_map)248 void add_random_vertices(Graph& graph, RandomNumberGenerator& generator,
249 int vertices_to_create, int max_edges_per_vertex, VertexNameMap vname_map,
250 EdgeNameMap ename_map)
251 {
252
253 typedef typename boost::graph_traits< Graph >::vertex_descriptor Vertex;
254 typedef std::vector< Vertex > VertexList;
255
256 VertexList new_vertices;
257
258 for (int v_index = 0; v_index < vertices_to_create; ++v_index)
259 {
260
261 Vertex new_vertex = add_vertex(graph);
262 put(vname_map, new_vertex, generator());
263 new_vertices.push_back(new_vertex);
264 }
265
266 // Add edges for every new vertex. Care is taken to avoid parallel
267 // edges.
268 for (typename VertexList::const_iterator v_iter = new_vertices.begin();
269 v_iter != new_vertices.end(); ++v_iter)
270 {
271
272 Vertex source_vertex = *v_iter;
273 int edges_for_vertex
274 = (std::min)((int)(generator() % max_edges_per_vertex) + 1,
275 (int)num_vertices(graph));
276
277 while (edges_for_vertex > 0)
278 {
279
280 Vertex target_vertex = random_vertex(graph, generator);
281
282 if (source_vertex == target_vertex)
283 {
284 continue;
285 }
286
287 BGL_FORALL_OUTEDGES_T(source_vertex, edge, graph, Graph)
288 {
289 if (target(edge, graph) == target_vertex)
290 {
291 continue;
292 }
293 }
294
295 put(ename_map, add_edge(source_vertex, target_vertex, graph).first,
296 generator());
297
298 edges_for_vertex--;
299 }
300 }
301 }
302
has_subgraph_string(std::string set_string)303 bool has_subgraph_string(std::string set_string)
304 {
305 return (std::find(simple_subgraph_list.begin(), simple_subgraph_list.end(),
306 set_string)
307 != simple_subgraph_list.end());
308 }
309
main(int argc,char * argv[])310 int main(int argc, char* argv[])
311 {
312 int vertices_to_create = 10;
313 int max_edges_per_vertex = 2;
314 std::size_t random_seed = time(0);
315
316 if (argc > 1)
317 {
318 vertices_to_create = boost::lexical_cast< int >(argv[1]);
319 }
320
321 if (argc > 2)
322 {
323 max_edges_per_vertex = boost::lexical_cast< int >(argv[2]);
324 }
325
326 if (argc > 3)
327 {
328 output_graphs = boost::lexical_cast< bool >(argv[3]);
329 }
330
331 if (argc > 4)
332 {
333 random_seed = boost::lexical_cast< std::size_t >(argv[4]);
334 }
335
336 boost::minstd_rand generator(random_seed);
337
338 // Using a vecS graph here so that we don't have to mess around with
339 // a vertex index map; it will be implicit.
340 typedef boost::adjacency_list< boost::listS, boost::vecS, boost::directedS,
341 boost::property< boost::vertex_name_t, unsigned int,
342 boost::property< boost::vertex_index_t, unsigned int > >,
343 boost::property< boost::edge_name_t, unsigned int > >
344 Graph;
345
346 typedef boost::property_map< Graph, boost::vertex_name_t >::type
347 VertexNameMap;
348 typedef boost::property_map< Graph, boost::edge_name_t >::type EdgeNameMap;
349
350 // Generate a random common subgraph and then add random vertices
351 // and edges to the two parent graphs.
352 Graph common_subgraph, graph1, graph2;
353
354 VertexNameMap vname_map_common = get(boost::vertex_name, common_subgraph);
355 VertexNameMap vname_map1 = get(boost::vertex_name, graph1);
356 VertexNameMap vname_map2 = get(boost::vertex_name, graph2);
357
358 EdgeNameMap ename_map_common = get(boost::edge_name, common_subgraph);
359 EdgeNameMap ename_map1 = get(boost::edge_name, graph1);
360 EdgeNameMap ename_map2 = get(boost::edge_name, graph2);
361
362 for (int vindex = 0; vindex < vertices_to_create; ++vindex)
363 {
364 put(vname_map_common, add_vertex(common_subgraph), generator());
365 }
366
367 BGL_FORALL_VERTICES(source_vertex, common_subgraph, Graph)
368 {
369
370 BGL_FORALL_VERTICES(target_vertex, common_subgraph, Graph)
371 {
372
373 if (source_vertex != target_vertex)
374 {
375 put(ename_map_common,
376 add_edge(source_vertex, target_vertex, common_subgraph)
377 .first,
378 generator());
379 }
380 }
381 }
382
383 boost::randomize_property< boost::vertex_name_t >(
384 common_subgraph, generator);
385 boost::randomize_property< boost::edge_name_t >(common_subgraph, generator);
386
387 boost::copy_graph(common_subgraph, graph1);
388 boost::copy_graph(common_subgraph, graph2);
389
390 // Randomly add vertices and edges to graph1 and graph2.
391 add_random_vertices(graph1, generator, vertices_to_create,
392 max_edges_per_vertex, vname_map1, ename_map1);
393
394 add_random_vertices(graph2, generator, vertices_to_create,
395 max_edges_per_vertex, vname_map2, ename_map2);
396
397 if (output_graphs)
398 {
399
400 std::fstream file_graph1("graph1.dot", std::fstream::out),
401 file_graph2("graph2.dot", std::fstream::out),
402 file_common_subgraph(
403 "expected_common_subgraph.dot", std::fstream::out);
404
405 write_graphviz(file_graph1, graph1, make_label_writer(vname_map1),
406 make_label_writer(ename_map1));
407
408 write_graphviz(file_graph2, graph2, make_label_writer(vname_map2),
409 make_label_writer(ename_map2));
410
411 write_graphviz(file_common_subgraph, common_subgraph,
412 make_label_writer(get(boost::vertex_name, common_subgraph)),
413 make_label_writer(get(boost::edge_name, common_subgraph)));
414 }
415
416 std::cout << "Searching for common subgraph of size "
417 << num_vertices(common_subgraph) << std::endl;
418
419 test_callback< Graph > user_callback(common_subgraph, graph1, graph2);
420
421 boost::mcgregor_common_subgraphs(graph1, graph2, true, user_callback,
422 boost::edges_equivalent(
423 boost::make_property_map_equivalent(ename_map1, ename_map2))
424 .vertices_equivalent(
425 boost::make_property_map_equivalent(vname_map1, vname_map2)));
426
427 BOOST_TEST(was_common_subgraph_found);
428
429 // Test maximum and unique variants on known graphs
430 Graph graph_simple1, graph_simple2;
431 simple_callback< Graph > user_callback_simple(graph_simple1);
432
433 VertexNameMap vname_map_simple1 = get(boost::vertex_name, graph_simple1);
434 VertexNameMap vname_map_simple2 = get(boost::vertex_name, graph_simple2);
435
436 put(vname_map_simple1, add_vertex(graph_simple1), 1);
437 put(vname_map_simple1, add_vertex(graph_simple1), 2);
438 put(vname_map_simple1, add_vertex(graph_simple1), 3);
439
440 add_edge(0, 1, graph_simple1);
441 add_edge(0, 2, graph_simple1);
442 add_edge(1, 2, graph_simple1);
443
444 put(vname_map_simple2, add_vertex(graph_simple2), 1);
445 put(vname_map_simple2, add_vertex(graph_simple2), 2);
446 put(vname_map_simple2, add_vertex(graph_simple2), 3);
447 put(vname_map_simple2, add_vertex(graph_simple2), 4);
448
449 add_edge(0, 1, graph_simple2);
450 add_edge(0, 2, graph_simple2);
451 add_edge(1, 2, graph_simple2);
452 add_edge(1, 3, graph_simple2);
453
454 // Unique subgraphs
455 std::cout << "Searching for unique subgraphs" << std::endl;
456 boost::mcgregor_common_subgraphs_unique(graph_simple1, graph_simple2, true,
457 user_callback_simple,
458 boost::vertices_equivalent(boost::make_property_map_equivalent(
459 vname_map_simple1, vname_map_simple2)));
460
461 BOOST_TEST(has_subgraph_string("0,0 1,1 "));
462 BOOST_TEST(has_subgraph_string("0,0 1,1 2,2 "));
463 BOOST_TEST(has_subgraph_string("0,0 2,2 "));
464 BOOST_TEST(has_subgraph_string("1,1 2,2 "));
465
466 if (output_graphs)
467 {
468 std::copy(simple_subgraph_list.begin(), simple_subgraph_list.end(),
469 std::ostream_iterator< std::string >(std::cout, "\n"));
470
471 std::cout << std::endl;
472 }
473
474 simple_subgraph_list.clear();
475
476 // Maximum subgraphs
477 std::cout << "Searching for maximum subgraphs" << std::endl;
478 boost::mcgregor_common_subgraphs_maximum(graph_simple1, graph_simple2, true,
479 user_callback_simple,
480 boost::vertices_equivalent(boost::make_property_map_equivalent(
481 vname_map_simple1, vname_map_simple2)));
482
483 BOOST_TEST(has_subgraph_string("0,0 1,1 2,2 "));
484
485 if (output_graphs)
486 {
487 std::copy(simple_subgraph_list.begin(), simple_subgraph_list.end(),
488 std::ostream_iterator< std::string >(std::cout, "\n"));
489
490 std::cout << std::endl;
491 }
492
493 simple_subgraph_list.clear();
494
495 // Maximum, unique subgraphs
496 std::cout << "Searching for maximum unique subgraphs" << std::endl;
497 boost::mcgregor_common_subgraphs_maximum_unique(graph_simple1,
498 graph_simple2, true, user_callback_simple,
499 boost::vertices_equivalent(boost::make_property_map_equivalent(
500 vname_map_simple1, vname_map_simple2)));
501
502 BOOST_TEST(simple_subgraph_list.size() == 1);
503 BOOST_TEST(has_subgraph_string("0,0 1,1 2,2 "));
504
505 if (output_graphs)
506 {
507 std::copy(simple_subgraph_list.begin(), simple_subgraph_list.end(),
508 std::ostream_iterator< std::string >(std::cout, "\n"));
509
510 std::cout << std::endl;
511 }
512
513 return boost::report_errors();
514 }
515