1 //=======================================================================
2 // Copyright 2009 Trustees of Indiana University.
3 // Authors: Michael Hansen
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 //=======================================================================
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/test/minimal.hpp>
28 bool was_common_subgraph_found
= false, output_graphs
= false;
29 std::vector
<std::string
> simple_subgraph_list
;
31 // Callback that compares incoming graphs to the supplied common
33 template <typename Graph
>
34 struct test_callback
{
36 test_callback(Graph
& common_subgraph
,
38 const Graph
& graph2
) :
41 m_common_subgraph(common_subgraph
) { }
43 template <typename CorrespondenceMapFirstToSecond
,
44 typename CorrespondenceMapSecondToFirst
>
45 bool operator()(CorrespondenceMapFirstToSecond correspondence_map_1_to_2
,
46 CorrespondenceMapSecondToFirst correspondence_map_2_to_1
,
47 typename
boost::graph_traits
<Graph
>::vertices_size_type subgraph_size
) {
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
;
53 typedef typename
boost::property_map
<Graph
, boost::vertex_index_t
>::type VertexIndexMap
;
54 typedef typename
boost::property_map
<Graph
, boost::vertex_name_t
>::type VertexNameMap
;
55 typedef typename
boost::property_map
<Graph
, boost::edge_name_t
>::type EdgeNameMap
;
57 if (subgraph_size
!= num_vertices(m_common_subgraph
)) {
61 // Fill membership maps for both graphs
62 typedef boost::shared_array_property_map
<bool, VertexIndexMap
> MembershipMap
;
64 MembershipMap
membership_map1(num_vertices(m_graph1
),
65 get(boost::vertex_index
, m_graph1
));
67 MembershipMap
membership_map2(num_vertices(m_graph2
),
68 get(boost::vertex_index
, m_graph2
));
70 boost::fill_membership_map
<Graph
>(m_graph1
, correspondence_map_1_to_2
, membership_map1
);
71 boost::fill_membership_map
<Graph
>(m_graph2
, correspondence_map_2_to_1
, membership_map2
);
73 // Generate filtered graphs using membership maps
74 typedef typename
boost::membership_filtered_graph_traits
<Graph
, MembershipMap
>::graph_type
75 MembershipFilteredGraph
;
77 MembershipFilteredGraph subgraph1
=
78 boost::make_membership_filtered_graph(m_graph1
, membership_map1
);
80 MembershipFilteredGraph subgraph2
=
81 boost::make_membership_filtered_graph(m_graph2
, membership_map2
);
83 VertexIndexMap vindex_map1
= get(boost::vertex_index
, subgraph1
);
84 VertexIndexMap vindex_map2
= get(boost::vertex_index
, subgraph2
);
86 VertexNameMap vname_map_common
= get(boost::vertex_name
, m_common_subgraph
);
87 VertexNameMap vname_map1
= get(boost::vertex_name
, subgraph1
);
88 VertexNameMap vname_map2
= get(boost::vertex_name
, subgraph2
);
90 EdgeNameMap ename_map_common
= get(boost::edge_name
, m_common_subgraph
);
91 EdgeNameMap ename_map1
= get(boost::edge_name
, subgraph1
);
92 EdgeNameMap ename_map2
= get(boost::edge_name
, subgraph2
);
94 // Verify that subgraph1 matches the supplied common subgraph
95 BGL_FORALL_VERTICES_T(vertex1
, subgraph1
, MembershipFilteredGraph
) {
97 Vertex vertex_common
= vertex(get(vindex_map1
, vertex1
), m_common_subgraph
);
100 if (get(vname_map_common
, vertex_common
) != get(vname_map1
, vertex1
)) {
107 BGL_FORALL_VERTICES_T(vertex1_2
, subgraph1
, MembershipFilteredGraph
) {
109 Vertex vertex_common2
= vertex(get(vindex_map1
, vertex1_2
), m_common_subgraph
);
110 EdgeInfo edge_common
= edge(vertex_common
, vertex_common2
, m_common_subgraph
);
111 EdgeInfo edge1
= edge(vertex1
, vertex1_2
, subgraph1
);
113 if ((edge_common
.second
!= edge1
.second
) ||
114 ((edge_common
.second
&& edge1
.second
) &&
115 (get(ename_map_common
, edge_common
.first
) != get(ename_map1
, edge1
.first
)))) {
123 } // BGL_FORALL_VERTICES_T (subgraph1)
125 // Verify that subgraph2 matches the supplied common subgraph
126 BGL_FORALL_VERTICES_T(vertex2
, subgraph2
, MembershipFilteredGraph
) {
128 Vertex vertex_common
= vertex(get(vindex_map2
, vertex2
), m_common_subgraph
);
130 // Match vertex names
131 if (get(vname_map_common
, vertex_common
) != get(vname_map2
, vertex2
)) {
138 BGL_FORALL_VERTICES_T(vertex2_2
, subgraph2
, MembershipFilteredGraph
) {
140 Vertex vertex_common2
= vertex(get(vindex_map2
, vertex2_2
), m_common_subgraph
);
141 EdgeInfo edge_common
= edge(vertex_common
, vertex_common2
, m_common_subgraph
);
142 EdgeInfo edge2
= edge(vertex2
, vertex2_2
, subgraph2
);
144 if ((edge_common
.second
!= edge2
.second
) ||
145 ((edge_common
.second
&& edge2
.second
) &&
146 (get(ename_map_common
, edge_common
.first
) != get(ename_map2
, edge2
.first
)))) {
154 } // BGL_FORALL_VERTICES_T (subgraph2)
156 // Check isomorphism just to be thorough
157 if (verify_isomorphism(subgraph1
, subgraph2
, correspondence_map_1_to_2
)) {
159 was_common_subgraph_found
= true;
163 std::fstream
file_subgraph("found_common_subgraph.dot", std::fstream::out
);
164 write_graphviz(file_subgraph
, subgraph1
,
165 make_label_writer(get(boost::vertex_name
, m_graph1
)),
166 make_label_writer(get(boost::edge_name
, m_graph1
)));
180 const Graph
& m_graph1
, m_graph2
;
181 Graph
& m_common_subgraph
;
184 template <typename Graph
>
185 struct simple_callback
{
187 simple_callback(const Graph
& graph1
) :
190 template <typename CorrespondenceMapFirstToSecond
,
191 typename CorrespondenceMapSecondToFirst
>
192 bool operator()(CorrespondenceMapFirstToSecond correspondence_map_1_to_2
,
193 CorrespondenceMapSecondToFirst
/*correspondence_map_2_to_1*/,
194 typename
boost::graph_traits
<Graph
>::vertices_size_type
/*subgraph_size*/) {
196 typedef typename
boost::graph_traits
<Graph
>::vertex_descriptor Vertex
;
198 std::stringstream subgraph_string
;
200 BGL_FORALL_VERTICES_T(vertex1
, m_graph1
, Graph
) {
202 Vertex vertex2
= get(correspondence_map_1_to_2
, vertex1
);
204 if (vertex2
!= boost::graph_traits
<Graph
>::null_vertex()) {
205 subgraph_string
<< vertex1
<< "," << vertex2
<< " ";
210 simple_subgraph_list
.push_back(subgraph_string
.str());
216 const Graph
& m_graph1
;
220 template <typename Graph
,
221 typename RandomNumberGenerator
,
222 typename VertexNameMap
,
223 typename EdgeNameMap
>
224 void add_random_vertices(Graph
& graph
, RandomNumberGenerator
& generator
,
225 int vertices_to_create
, int max_edges_per_vertex
,
226 VertexNameMap vname_map
, EdgeNameMap ename_map
) {
228 typedef typename
boost::graph_traits
<Graph
>::vertex_descriptor Vertex
;
229 typedef std::vector
<Vertex
> VertexList
;
231 VertexList new_vertices
;
233 for (int v_index
= 0; v_index
< vertices_to_create
; ++v_index
) {
235 Vertex new_vertex
= add_vertex(graph
);
236 put(vname_map
, new_vertex
, generator());
237 new_vertices
.push_back(new_vertex
);
241 // Add edges for every new vertex. Care is taken to avoid parallel
243 for (typename
VertexList::const_iterator v_iter
= new_vertices
.begin();
244 v_iter
!= new_vertices
.end(); ++v_iter
) {
246 Vertex source_vertex
= *v_iter
;
247 int edges_for_vertex
= (std::min
)((int)(generator() % max_edges_per_vertex
) + 1,
248 (int)num_vertices(graph
));
250 while (edges_for_vertex
> 0) {
252 Vertex target_vertex
= random_vertex(graph
, generator
);
254 if (source_vertex
== target_vertex
) {
258 BGL_FORALL_OUTEDGES_T(source_vertex
, edge
, graph
, Graph
) {
259 if (target(edge
, graph
) == target_vertex
) {
264 put(ename_map
, add_edge(source_vertex
, target_vertex
, graph
).first
,
272 bool has_subgraph_string(std::string set_string
) {
273 return (std::find(simple_subgraph_list
.begin(), simple_subgraph_list
.end(),
274 set_string
) != simple_subgraph_list
.end());
277 int test_main (int argc
, char *argv
[]) {
278 int vertices_to_create
= 10;
279 int max_edges_per_vertex
= 2;
280 std::size_t random_seed
= time(0);
283 vertices_to_create
= boost::lexical_cast
<int>(argv
[1]);
287 max_edges_per_vertex
= boost::lexical_cast
<int>(argv
[2]);
291 output_graphs
= boost::lexical_cast
<bool>(argv
[3]);
295 random_seed
= boost::lexical_cast
<std::size_t>(argv
[4]);
298 boost::minstd_rand
generator(random_seed
);
300 // Using a vecS graph here so that we don't have to mess around with
301 // a vertex index map; it will be implicit.
302 typedef boost::adjacency_list
<boost::listS
, boost::vecS
, boost::directedS
,
303 boost::property
<boost::vertex_name_t
, unsigned int,
304 boost::property
<boost::vertex_index_t
, unsigned int> >,
305 boost::property
<boost::edge_name_t
, unsigned int> > Graph
;
307 typedef boost::property_map
<Graph
, boost::vertex_name_t
>::type VertexNameMap
;
308 typedef boost::property_map
<Graph
, boost::edge_name_t
>::type EdgeNameMap
;
310 // Generate a random common subgraph and then add random vertices
311 // and edges to the two parent graphs.
312 Graph common_subgraph
, graph1
, graph2
;
314 VertexNameMap vname_map_common
= get(boost::vertex_name
, common_subgraph
);
315 VertexNameMap vname_map1
= get(boost::vertex_name
, graph1
);
316 VertexNameMap vname_map2
= get(boost::vertex_name
, graph2
);
318 EdgeNameMap ename_map_common
= get(boost::edge_name
, common_subgraph
);
319 EdgeNameMap ename_map1
= get(boost::edge_name
, graph1
);
320 EdgeNameMap ename_map2
= get(boost::edge_name
, graph2
);
322 for (int vindex
= 0; vindex
< vertices_to_create
; ++vindex
) {
323 put(vname_map_common
, add_vertex(common_subgraph
), generator());
326 BGL_FORALL_VERTICES(source_vertex
, common_subgraph
, Graph
) {
328 BGL_FORALL_VERTICES(target_vertex
, common_subgraph
, Graph
) {
330 if (source_vertex
!= target_vertex
) {
331 put(ename_map_common
,
332 add_edge(source_vertex
, target_vertex
, common_subgraph
).first
,
338 boost::randomize_property
<boost::vertex_name_t
>(common_subgraph
, generator
);
339 boost::randomize_property
<boost::edge_name_t
>(common_subgraph
, generator
);
341 boost::copy_graph(common_subgraph
, graph1
);
342 boost::copy_graph(common_subgraph
, graph2
);
344 // Randomly add vertices and edges to graph1 and graph2.
345 add_random_vertices(graph1
, generator
, vertices_to_create
,
346 max_edges_per_vertex
, vname_map1
, ename_map1
);
348 add_random_vertices(graph2
, generator
, vertices_to_create
,
349 max_edges_per_vertex
, vname_map2
, ename_map2
);
353 std::fstream
file_graph1("graph1.dot", std::fstream::out
),
354 file_graph2("graph2.dot", std::fstream::out
),
355 file_common_subgraph("expected_common_subgraph.dot", std::fstream::out
);
357 write_graphviz(file_graph1
, graph1
,
358 make_label_writer(vname_map1
),
359 make_label_writer(ename_map1
));
361 write_graphviz(file_graph2
, graph2
,
362 make_label_writer(vname_map2
),
363 make_label_writer(ename_map2
));
365 write_graphviz(file_common_subgraph
, common_subgraph
,
366 make_label_writer(get(boost::vertex_name
, common_subgraph
)),
367 make_label_writer(get(boost::edge_name
, common_subgraph
)));
371 std::cout
<< "Searching for common subgraph of size " <<
372 num_vertices(common_subgraph
) << std::endl
;
374 test_callback
<Graph
> user_callback(common_subgraph
, graph1
, graph2
);
376 boost::mcgregor_common_subgraphs(graph1
, graph2
, true, user_callback
,
377 boost::edges_equivalent(boost::make_property_map_equivalent(ename_map1
, ename_map2
)).
378 vertices_equivalent(boost::make_property_map_equivalent(vname_map1
, vname_map2
)));
380 BOOST_CHECK(was_common_subgraph_found
);
382 // Test maximum and unique variants on known graphs
383 Graph graph_simple1
, graph_simple2
;
384 simple_callback
<Graph
> user_callback_simple(graph_simple1
);
386 VertexNameMap vname_map_simple1
= get(boost::vertex_name
, graph_simple1
);
387 VertexNameMap vname_map_simple2
= get(boost::vertex_name
, graph_simple2
);
389 put(vname_map_simple1
, add_vertex(graph_simple1
), 1);
390 put(vname_map_simple1
, add_vertex(graph_simple1
), 2);
391 put(vname_map_simple1
, add_vertex(graph_simple1
), 3);
393 add_edge(0, 1, graph_simple1
);
394 add_edge(0, 2, graph_simple1
);
395 add_edge(1, 2, graph_simple1
);
397 put(vname_map_simple2
, add_vertex(graph_simple2
), 1);
398 put(vname_map_simple2
, add_vertex(graph_simple2
), 2);
399 put(vname_map_simple2
, add_vertex(graph_simple2
), 3);
400 put(vname_map_simple2
, add_vertex(graph_simple2
), 4);
402 add_edge(0, 1, graph_simple2
);
403 add_edge(0, 2, graph_simple2
);
404 add_edge(1, 2, graph_simple2
);
405 add_edge(1, 3, graph_simple2
);
408 std::cout
<< "Searching for unique subgraphs" << std::endl
;
409 boost::mcgregor_common_subgraphs_unique(graph_simple1
, graph_simple2
,
410 true, user_callback_simple
,
411 boost::vertices_equivalent(boost::make_property_map_equivalent(vname_map_simple1
, vname_map_simple2
)));
413 BOOST_CHECK(has_subgraph_string("0,0 1,1 "));
414 BOOST_CHECK(has_subgraph_string("0,0 1,1 2,2 "));
415 BOOST_CHECK(has_subgraph_string("0,0 2,2 "));
416 BOOST_CHECK(has_subgraph_string("1,1 2,2 "));
419 std::copy(simple_subgraph_list
.begin(), simple_subgraph_list
.end(),
420 std::ostream_iterator
<std::string
>(std::cout
, "\n"));
422 std::cout
<< std::endl
;
425 simple_subgraph_list
.clear();
428 std::cout
<< "Searching for maximum subgraphs" << std::endl
;
429 boost::mcgregor_common_subgraphs_maximum(graph_simple1
, graph_simple2
,
430 true, user_callback_simple
,
431 boost::vertices_equivalent(boost::make_property_map_equivalent(vname_map_simple1
, vname_map_simple2
)));
433 BOOST_CHECK(has_subgraph_string("0,0 1,1 2,2 "));
436 std::copy(simple_subgraph_list
.begin(), simple_subgraph_list
.end(),
437 std::ostream_iterator
<std::string
>(std::cout
, "\n"));
439 std::cout
<< std::endl
;
442 simple_subgraph_list
.clear();
444 // Maximum, unique subgraphs
445 std::cout
<< "Searching for maximum unique subgraphs" << std::endl
;
446 boost::mcgregor_common_subgraphs_maximum_unique(graph_simple1
, graph_simple2
,
447 true, user_callback_simple
,
448 boost::vertices_equivalent(boost::make_property_map_equivalent(vname_map_simple1
, vname_map_simple2
)));
450 BOOST_CHECK(simple_subgraph_list
.size() == 1);
451 BOOST_CHECK(has_subgraph_string("0,0 1,1 2,2 "));
454 std::copy(simple_subgraph_list
.begin(), simple_subgraph_list
.end(),
455 std::ostream_iterator
<std::string
>(std::cout
, "\n"));
457 std::cout
<< std::endl
;