• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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