From b0e4e9fa017888a9e2ad5d258ad6c8f36ec155da Mon Sep 17 00:00:00 2001
From: Jeremiah Willcock
Date: Tue, 30 Jun 2009 17:08:49 +0000
Subject: [PATCH] Merged patches mentioned in comments 34-53 from ticket 3134
into release branch; refs #3134
[SVN r54540]
---
doc/compressed_sparse_row.html | 51 +-
doc/mcgregor_common_subgraphs.html | 451 +++++++
doc/table_of_contents.html | 1 +
example/Jamfile.v2 | 1 +
example/mcgregor_subgraphs_example.cpp | 148 +++
.../graph/compressed_sparse_row_graph.hpp | 516 +++++---
.../boost/graph/detail/indexed_properties.hpp | 32 +
include/boost/graph/filtered_graph.hpp | 17 +
include/boost/graph/graph_utility.hpp | 37 +
.../boost/graph/mcgregor_common_subgraphs.hpp | 1142 +++++++++++++++++
test/CMakeLists.txt | 1 +
test/Jamfile.v2 | 1 +
test/mcgregor_subgraphs_test.cpp | 470 +++++++
13 files changed, 2666 insertions(+), 202 deletions(-)
create mode 100644 doc/mcgregor_common_subgraphs.html
create mode 100644 example/mcgregor_subgraphs_example.cpp
create mode 100644 include/boost/graph/mcgregor_common_subgraphs.hpp
create mode 100644 test/mcgregor_subgraphs_test.cpp
diff --git a/doc/compressed_sparse_row.html b/doc/compressed_sparse_row.html
index 1f7d70b6..a1d8980d 100644
--- a/doc/compressed_sparse_row.html
+++ b/doc/compressed_sparse_row.html
@@ -294,6 +294,14 @@ edge_descriptor add_edge(vertex_descriptor src, vertex_d
template<typename InputIterator, typename Graph>
void add_edges(InputIterator first, InputIterator last, compressed_sparse_row_graph& g);
+(new interface only)
+template<typename BidirectionalIterator, typename Graph>
+void add_edges_sorted(BidirectionalIterator first, BidirectionalIterator last, compressed_sparse_row_graph& g);
+
+(new interface only)
+template<typename BidirectionalIterator, typename EPIter, typename Graph>
+void add_edges_sorted(BidirectionalIterator first, BidirectionalIterator last, EPIter ep_iter, compressed_sparse_row_graph& g);
+
} // end namespace boost
@@ -918,7 +926,7 @@ edge_descriptor add_edge(vertex_descriptor src, vertex_descriptor tgt, compresse
template<typename InputIterator>
-edge_descriptor add_edges(InputIterator first, InputIterator last, compressed_sparse_row_graph& g)
+void add_edges(InputIterator first, InputIterator last, compressed_sparse_row_graph& g)
@@ -931,6 +939,47 @@ edge_descriptor add_edges(InputIterator first, InputIterator last, compressed_sp
(This function is only provided by the new interface.)
+
+
+
+template<typename BidirectionalIterator>
+void add_edges_sorted(BidirectionalIterator first, BidirectionalIterator last, compressed_sparse_row_graph& g)
+
+
+
+ Add a range of edges (from first to last) to the graph.
+ The BidirectionalIterator must be a model of BidirectionalIterator
+ whose value_type is an std::pair of integer
+ values. These integer values are the source and target vertices of the
+ new edges. The edges must be sorted in increasing order by source vertex
+ index.
+ (This function is only provided by the new interface.)
+
+
+
+
+
+template<typename BidirectionalIterator, typename EPIter>
+void add_edges_sorted(BidirectionalIterator first, BidirectionalIterator last, EPIter ep_iter, compressed_sparse_row_graph& g)
+
+
+
+ Add a range of edges (from first to last) to the graph.
+ The BidirectionalIterator and EPIter must be models of
+ BidirectionalIterator.
+ The value_type of the BidirectionalIterator must be
+ an std::pair of integer
+ values. These integer values are the source and target vertices of the
+ new edges.
+ The value_type of the EPIter must be the edge
+ property type of the graph.
+ The edges must be sorted in increasing order by source vertex
+ index.
+ (This function is only provided by the new interface.)
+
+
Example
diff --git a/doc/mcgregor_common_subgraphs.html b/doc/mcgregor_common_subgraphs.html
new file mode 100644
index 00000000..291639f5
--- /dev/null
+++ b/doc/mcgregor_common_subgraphs.html
@@ -0,0 +1,451 @@
+
+
+
+
+ Boost Graph Library: McGregor Common Subgraphs
+
+
+
+
+
+
+ mcgregor_common_subgraphs
+
+
+
+template <typename GraphFirst,
+ typename GraphSecond,
+ typename SubGraphCallback,
+ typename Param,
+ typename Tag,
+ typename Rest>
+ void mcgregor_common_subgraphs
+ (const GraphFirst& graph1,
+ const GraphSecond& graph2,
+ SubGraphCallback user_callback,
+ bool only_connected_subgraphs,
+ const bgl_named_params& params);
+
+
+template <typename GraphFirst,
+ typename GraphSecond,
+ typename VertexIndexMapFirst,
+ typename VertexIndexMapSecond,
+ typename EdgeEquivalencePredicate,
+ typename VertexEquivalencePredicate,
+ typename SubGraphCallback>
+ void mcgregor_common_subgraphs
+ (const GraphFirst& graph1,
+ const GraphSecond& graph2,
+ const VertexIndexMapFirst vindex_map1,
+ const VertexIndexMapSecond vindex_map2,
+ EdgeEquivalencePredicate edges_equivalent,
+ VertexEquivalencePredicate vertices_equivalent,
+ bool only_connected_subgraphs,
+ SubGraphCallback user_callback);
+
+
+
+ This algorithm finds all common subgraphs
+ between graph1 and graph2 and outputs them
+ to user_callback. The edges_equivalent
+ and vertices_equivalent predicates are used to
+ determine edges and vertex equivalence between the two graphs.
+ To use property maps for equivalence, look at
+ the make_property_map_equivalent
+ function. By
+ default, always_equivalent
+ is used, which returns true for any pair of edges or vertices.
+
+
+ McGregor's algorithm does a depth-first search on the space of
+ possible common subgraphs. At each level, every unvisited pair
+ of vertices from graph1 and graph2 are checked
+ to see if they can extend the current subgraph. This is done in
+ three steps (assume vertex1 is
+ from graph1 and vertex2 is
+ from graph2):
+
+ -
+ Verify that the vertex1 and vertex2 are
+ equivalent using the vertices_equivalent predicate.
+
+ -
+ For every vertex pair
+ (existing_vertex1, existing_vertex2) in
+ the current subgraph, ensure that any edges
+ between vertex1 and existing_vertex1
+ in graph1 and between vertex2
+ and existing_vertex2 in graph2 match
+ (i.e. either both exist of both don't exist). If both edges
+ exist, they are checked for equivalence using
+ the edges_equivalent predicate.
+
+ -
+ Lastly (and optionally), make sure that the new subgraph
+ vertex has at least one edge connecting it to the existing
+ subgraph. When the only_connected_subgraphs
+ parameter is false, this step is skipped.
+
+
+
+
+ Where Defined
+ boost/graph/mcgregor_common_subgraphs.hpp
+
+ All functions are defined in the boost namespace.
+
+
+ Parameters
+
+ IN: const GraphFirst& graph1
+
+ The first graph of the pair to be searched. The
+ type GraphFirst must be a model of
+ Vertex List Graph
+ and Incidence Graph. All
+ parameters with a "1"
+ (i.e. vindex_map1, correspondence_map_1_to_2)
+ use this graph's vertices as keys.
+
+
+ IN: const GraphSecond& graph2
+
+ The second graph of the pair to be searched. The
+ type Graphsecond must be a model of
+ Vertex List Graph
+ and Incidence Graph. All
+ parameters with a "2"
+ (i.e. vindex_map2, correspondence_map_2_to_1)
+ use this graph's vertices as keys.
+
+
+ IN: bool only_connected_subgraphs
+
+ If true, subgraphs are expanded only when matching vertices
+ have at least one edge connecting them to the existing subgraph.
+
+
+ OUT: SubGraphCallback user_callback
+
+ A function object that will be invoked when a subgraph has been discovered. The operator() must have the following form:
+
+template <typename CorrespondenceMapFirstToSecond,
+ typename CorrespondenceMapSecondToFirst>
+bool operator()(CorrespondenceMapFirstToSecond correspondence_map_1_to_2,
+ CorrespondenceMapSecondToFirst correspondence_map_2_to_1,
+ typename graph_traits<GraphFirst>::vertices_size_type subgraph_size);
+
+ Both the CorrespondenceMapFirstToSecond
+ and CorresondenceMapSecondToFirst types are models
+ of Readable
+ Property Map and map equivalent vertices across the two
+ graphs given to mcgregor_common_subgraphs. For
+ example, if vertex1 is
+ from graph1, vertex2 is from graph2,
+ and the vertices can be considered equivalent in the subgraph,
+ then get(correspondence_map_1_to_2, vertex1) will
+ be vertex2 and get(correspondence_map_2_to_1,
+ vertex2) will be vertex1. If any vertex,
+ say vertex1 in graph1, doesn't match a vertex
+ in the other graph (graph2 in this example),
+ then get(correspondence_map_1_to_2, vertex1) will
+ be graph_traits<GraphSecond>::null_vertex().
+ Likewise for any un-matched vertices from graph2,
+ get(correspondence_map_2_to_1, vertex2) will
+ be graph_traits<GraphFirst>::null_vertex().
+
The subgraph_size parameter is the number
+ of vertices in the subgraph, or the number of matched vertex
+ pairs contained in the correspondence maps. This can be used to
+ quickly filter out subgraphs whose sizes do not fall within the
+ desired range.
Returning false from the
+ callback will abort the search immediately. Otherwise, the
+ entire search space will be explored [1].
+
+
+ Named Parameters
+
+ IN: vertex_index1(VertexIndexMapFirst vindex_map1)
+
+ This maps each vertex to an integer in the range [0,
+ num_vertices(graph1)]. This is necessary for efficient storage
+ of the subgraphs. The type VertexIndexMapFirst must be a model of
+ Readable Property Map.
+
+ Default: get(vertex_index, graph1)
+
+
+ IN: vertex_index2(VertexIndexMapsecond vindex_map2)
+
+ This maps each vertex to an integer in the range [0,
+ num_vertices(graph2)]. This is necessary for efficient storage
+ of the subgraphs. The type VertexIndexMapFirst must be a model of
+ Readable Property Map.
+
+ Default: get(vertex_index, graph2)
+
+
+ IN: edges_equivalent(EdgeEquivalencePredicate edges_equivalent)
+
+ This function is used to determine if edges
+ between graph1 and graph2 are equivalent.
+ The EdgeEquivalencePredicate type must be a model
+ of Binary
+ Predicate and have argument types
+ of graph_traits<GraphFirst>::edge_descriptor
+ and graph_traits<GraphSecond>::edge_descriptor.
+ A return value of true indicates that the edges are
+ equivalent.
+ Default: always_equivalent
+
+
+ IN: vertices_equivalent(VertexEquivalencePredicate vertices_equivalent)
+
+ This function is used to determine if vertices
+ between graph1 and graph2 are equivalent.
+ The VertexEquivalencePredicate type must be a model
+ of Binary
+ Predicate and have argument types
+ of graph_traits<GraphFirst>::vertex_descriptor
+ and graph_traits<GraphSecond>::vertex_descriptor.
+ A return value of true indicates that the vertices are
+ equivalent.
+ Default: always_equivalent
+
+
+ Related Functions
+
+ Each mcgregor_common_subgraphs_* function below takes
+ the same parameters as mcgregor_common_subgraphs.
+
+ mcgregor_common_subgraphs_unique(...);
+
+ Keeps an internal cache of all discovered subgraphs and
+ only invokes the user_callback for unique
+ subgraphs. Returning false
+ from user_callback will terminate the search as
+ expected.
+
+
+ mcgregor_common_subgraphs_maximum(...);
+
+ Explores the entire search space and invokes
+ the user_callback afterward with each of the largest
+ discovered subgraphs. Returning false from
+ the user_callback will not terminate the search
+ because it is invoked after the search has been completed.
+
+
+ mcgregor_common_subgraphs_maximum_unique(...);
+
+ Explores the entire search space and invokes
+ the user_callback afterward with each of the largest,
+ unique discovered subgraphs. Returning false from
+ the user_callback will not terminate the search
+ because it is invoked after the search has been completed.
+
+
+ Utility Functions/Structs
+
+property_map_equivalent<PropertyMapFirst, PropertyMapSecond>
+ make_property_map_equivalent(const PropertyMapFirst property_map1, const PropertyMapSecond property_map2);
+
+
+ Returns a binary predicate function object
+ (property_map_equivalent<PropertyMapFirst,
+ PropertyMapSecond>) that compares vertices or edges
+ between graphs using property maps. If, for
+ example, vertex1 and vertex2 are the two
+ parameters given when the function object is invoked,
+ the operator() is effectively:
+ return (get(m_property_map1, vertex1) == get(m_property_map2, vertex2));
+
+
+ struct always_equivalent
+
+ A binary function object that returns true for any pair of items.
+
+
+
+void fill_membership_map<GraphSecond>
+(const GraphFirst& graph1, const CorrespondenceMapFirstToSecond correspondence_map_1_to_2, MembershipMapFirst membership_map1);
+
+
+ Takes a subgraph (represented as a correspondence map) and fills
+ the vertex membership map (vertex -> bool) (true means
+ the vertex is present in the subgraph).
+ MembershipMapFirst must
+ model Writable
+ Property Map.
+
+
+
+typename membership_filtered_graph_traits<Graph, MembershipMap>::graph_type
+ make_membership_filtered_graph(const Graph& graph, MembershipMap& membership_map);
+
+
+ Creates a Filtered Graph from
+ a subgraph, represented here as a vertex membership map (vertex
+ -> bool where a value of true means that the vertex is
+ present in the subgraph). All edges between the included
+ vertices are preserved. See the example section for details.
+
+
+ Complexity
+
+ The time complexity for searching the entire space is O(V1 *
+ V2) where V1 is number of vertices in the first graph and
+ V2 is the number of vertices in the second graph.
+
+
+ Examples
+
+ Before calling any of the mcregor_common_subgraphs
+ algorithms, you must create a function object to act as a callback:
+
+
+template <typename GraphFirst,
+ typename GraphSecond>
+struct print_callback {
+
+ print_callback(const GraphFirst& graph1,
+ const GraphSecond& graph2) :
+ m_graph1(graph1), m_graph2(graph2) { }
+
+template <typename CorrespondenceMapFirstToSecond,
+ typename CorrespondenceMapSecondToFirst>
+ bool operator()(CorrespondenceMapFirstToSecond correspondence_map_1_to_2,
+ CorrespondenceMapSecondToFirst correspondence_map_2_to_1,
+ typename graph_traits<GraphFirst>::vertices_size_type subgraph_size) {
+
+
+ BGL_FORALL_VERTICES_T(vertex1, m_graph1, GraphFirst) {
+
+
+ if (get(correspondence_map_1_to_2, vertex1) != graph_traits<GraphSecond>::null_vertex()) {
+ std::cout << vertex1 << " <-> " << get(correspondence_map_1_to_2, vertex1) << std::endl;
+ }
+
+ }
+
+ std::cout << "---" << std::endl;
+
+ return (true);
+ }
+
+ private:
+ const GraphFirst& m_graph1;
+ const GraphSecond& m_graph2;
+
+};
+
+
+GraphFirst graph1;
+GraphSecond graph2;
+
+print_callback<GraphFirst, GraphSecond> my_callback(graph1, graph2);
+
+
+ Example 1
+
+ If all the vertices and edges in your graph are identical, you
+ can start enumerating subgraphs immediately:
+
+
+
+mcgregor_common_subgraphs(graph1, graph2, true, my_callback);
+
+
+ Example 2
+
+ If the vertices and edges of your graphs can be differentiated
+ with property maps, you can use
+ the make_property_map_equivalent function to create a
+ binary predicate for vertex or edge equivalence:
+
+
+
+
+property_map<GraphFirst, vertex_name_t> vname_map1 = get(vertex_name, graph1);
+property_map<GraphSecond, vertex_name_t> vname_map1 = get(vertex_name, graph2);
+
+property_map<GraphFirst, edge_name_t> ename_map1 = get(edge_name, graph1);
+property_map<GraphSecond, edge_name_t> ename_map1 = get(edge_name, graph2);
+
+
+mcgregor_common_subgraphs(graph1, graph2, true, my_callback,
+ edges_equivalent(make_property_map_equivalent(ename_map1, ename_map2)).
+ vertices_equivalent(make_property_map_equivalent(vname_map1, vname_map2)));
+
+
+ Example 3
+
+ There are some helper functions that can be used to obtain a
+ filtered graph from the correspondence maps given in your
+ callback:
+
+
+
+
+
+typedef shared_array_property_map MembershipMap;
+MembershipMap membership_map1(num_vertices(m_graph1), get(vertex_index, m_graph1));
+
+
+fill_membership_map<GraphSecond>(m_graph1, correspondence_map_1_to_2, membership_map1);
+
+
+typedef typename membership_filtered_graph_traits<GraphFirst, MembershipMap>::graph_type
+ MembershipFilteredGraph;
+
+MembershipFilteredGraph subgraph1 = make_membership_filtered_graph(m_graph1, membership_map1);
+
+
+BGL_FORALL_VERTICES_T(vertex1, subgraph1, MembershipFilteredGraph) {
+ std::cout << vertex1 << " is present in the subgraph of graph1" << std::endl;
+}
+
+
+ Notes
+
+ [1]
+ For mcgregor_common_subgraphs_maximum
+ and mcgregor_common_subgraphs_maximum_unique the entire
+ search space is always explored, so the return value of the
+ callback has no effect.
+
+
+
+ Copyright © 2009 Trustees of Indiana University
+
+
+
diff --git a/doc/table_of_contents.html b/doc/table_of_contents.html
index c395f897..da3bafd2 100644
--- a/doc/table_of_contents.html
+++ b/doc/table_of_contents.html
@@ -245,6 +245,7 @@
make_maximal_planar
lengauer_tarjan_dominator_tree
+ mcgregor_common_subgraphs
diff --git a/example/Jamfile.v2 b/example/Jamfile.v2
index cef472e3..e157a2a4 100644
--- a/example/Jamfile.v2
+++ b/example/Jamfile.v2
@@ -18,3 +18,4 @@ exe tiernan_print_cycles : tiernan_print_cycles.cpp ;
exe tiernan_girth_circumference : tiernan_girth_circumference.cpp ;
exe bron_kerbosch_print_cliques : bron_kerbosch_print_cliques.cpp ;
exe bron_kerbosch_clique_number : bron_kerbosch_clique_number.cpp ;
+exe mcgregor_subgraphs_example : mcgregor_subgraphs_example.cpp ;
diff --git a/example/mcgregor_subgraphs_example.cpp b/example/mcgregor_subgraphs_example.cpp
new file mode 100644
index 00000000..35a3b693
--- /dev/null
+++ b/example/mcgregor_subgraphs_example.cpp
@@ -0,0 +1,148 @@
+//=======================================================================
+// Copyright 2009 Trustees of Indiana University.
+// Authors: Michael Hansen
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//=======================================================================
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+using namespace boost;
+
+// Callback that looks for the first common subgraph whose size
+// matches the user's preference.
+template
+struct example_callback {
+
+ typedef typename graph_traits::vertices_size_type VertexSizeFirst;
+
+ example_callback(const Graph& graph1) :
+ m_graph1(graph1) { }
+
+ template
+ bool operator()(CorrespondenceMapFirstToSecond correspondence_map_1_to_2,
+ CorrespondenceMapSecondToFirst correspondence_map_2_to_1,
+ VertexSizeFirst subgraph_size) {
+
+ // Fill membership map for first graph
+ typedef typename property_map::type VertexIndexMap;
+ typedef shared_array_property_map MembershipMap;
+
+ MembershipMap membership_map1(num_vertices(m_graph1),
+ get(vertex_index, m_graph1));
+
+ fill_membership_map(m_graph1, correspondence_map_1_to_2, membership_map1);
+
+ // Generate filtered graphs using membership map
+ typedef typename membership_filtered_graph_traits::graph_type
+ MembershipFilteredGraph;
+
+ MembershipFilteredGraph subgraph1 =
+ make_membership_filtered_graph(m_graph1, membership_map1);
+
+ // Print the graph out to the console
+ std::cout << "Found common subgraph (size " << subgraph_size << ")" << std::endl;
+ print_graph(subgraph1);
+ std::cout << std::endl;
+
+ // Explore the entire space
+ return (true);
+ }
+
+private:
+ const Graph& m_graph1;
+ VertexSizeFirst m_max_subgraph_size;
+};
+
+int main (int argc, char *argv[]) {
+
+ // Using a vecS graph here so that we don't have to mess around with
+ // a vertex index map; it will be implicit.
+ typedef adjacency_list >,
+ property > Graph;
+
+ typedef graph_traits::vertex_descriptor Vertex;
+ typedef graph_traits::edge_descriptor Edge;
+
+ typedef property_map::type VertexNameMap;
+ typedef property_map::type EdgeNameMap;
+
+ // Test maximum and unique variants on known graphs
+ Graph graph_simple1, graph_simple2;
+ example_callback user_callback(graph_simple1);
+
+ VertexNameMap vname_map_simple1 = get(vertex_name, graph_simple1);
+ VertexNameMap vname_map_simple2 = get(vertex_name, graph_simple2);
+
+ // Graph that looks like a triangle
+ put(vname_map_simple1, add_vertex(graph_simple1), 1);
+ put(vname_map_simple1, add_vertex(graph_simple1), 2);
+ put(vname_map_simple1, add_vertex(graph_simple1), 3);
+
+ add_edge(0, 1, graph_simple1);
+ add_edge(0, 2, graph_simple1);
+ add_edge(1, 2, graph_simple1);
+
+ std::cout << "First graph:" << std::endl;
+ print_graph(graph_simple1);
+ std::cout << std::endl;
+
+ // Triangle with an extra vertex
+ put(vname_map_simple2, add_vertex(graph_simple2), 1);
+ put(vname_map_simple2, add_vertex(graph_simple2), 2);
+ put(vname_map_simple2, add_vertex(graph_simple2), 3);
+ put(vname_map_simple2, add_vertex(graph_simple2), 4);
+
+ add_edge(0, 1, graph_simple2);
+ add_edge(0, 2, graph_simple2);
+ add_edge(1, 2, graph_simple2);
+ add_edge(1, 3, graph_simple2);
+
+ std::cout << "Second graph:" << std::endl;
+ print_graph(graph_simple2);
+ std::cout << std::endl;
+
+ // All subgraphs
+ std::cout << "mcgregor_common_subgraphs:" << std::endl;
+ mcgregor_common_subgraphs
+ (graph_simple1, graph_simple2, true, user_callback,
+ vertices_equivalent(make_property_map_equivalent(vname_map_simple1, vname_map_simple2)));
+ std::cout << std::endl;
+
+ // Unique subgraphs
+ std::cout << "mcgregor_common_subgraphs_unique:" << std::endl;
+ mcgregor_common_subgraphs_unique
+ (graph_simple1, graph_simple2, true, user_callback,
+ vertices_equivalent(make_property_map_equivalent(vname_map_simple1, vname_map_simple2)));
+ std::cout << std::endl;
+
+ // Maximum subgraphs
+ std::cout << "mcgregor_common_subgraphs_maximum:" << std::endl;
+ mcgregor_common_subgraphs_maximum
+ (graph_simple1, graph_simple2, true, user_callback,
+ vertices_equivalent(make_property_map_equivalent(vname_map_simple1, vname_map_simple2)));
+ std::cout << std::endl;
+
+ // Maximum, unique subgraphs
+ std::cout << "mcgregor_common_subgraphs_maximum_unique:" << std::endl;
+ mcgregor_common_subgraphs_maximum_unique
+ (graph_simple1, graph_simple2, true, user_callback,
+ vertices_equivalent(make_property_map_equivalent(vname_map_simple1, vname_map_simple2)));
+
+ return 0;
+}
diff --git a/include/boost/graph/compressed_sparse_row_graph.hpp b/include/boost/graph/compressed_sparse_row_graph.hpp
index 98fe61ca..91ac4712 100644
--- a/include/boost/graph/compressed_sparse_row_graph.hpp
+++ b/include/boost/graph/compressed_sparse_row_graph.hpp
@@ -27,6 +27,7 @@
#include // For keep_all
#include
#include
+#include
#include
#include
#include
@@ -64,6 +65,11 @@ struct csr_graph_tag;
enum edges_are_sorted_t {edges_are_sorted};
#ifdef BOOST_GRAPH_USE_NEW_CSR_INTERFACE
+// A type (edges_are_sorted_global_t) and a value (edges_are_sorted_global)
+// used to indicate that the edge list passed into the CSR graph is already
+// sorted by source vertex.
+enum edges_are_sorted_global_t {edges_are_sorted_global};
+
// A type (edges_are_unsorted_t) and a value (edges_are_unsorted) used to
// indicate that the edge list passed into the CSR graph is not sorted by
// source vertex. This version caches the edge information in memory, and thus
@@ -76,13 +82,13 @@ enum edges_are_unsorted_t {edges_are_unsorted};
// memory but requires multi-pass capability on the iterators.
enum edges_are_unsorted_multi_pass_t {edges_are_unsorted_multi_pass};
-// A type (edges_are_unsorted_multi_pass_filtered_t) and a value
-// (edges_are_unsorted_multi_pass_filtered) used to indicate that the edge list
+// A type (edges_are_unsorted_multi_pass_global_t) and a value
+// (edges_are_unsorted_multi_pass_global) used to indicate that the edge list
// passed into the CSR graph is not sorted by source vertex. This version uses
// less memory but requires multi-pass capability on the iterators. The
-// filtering is done here because it is often faster and it greatly simplifies
-// handling of edge properties.
-enum edges_are_unsorted_multi_pass_filtered_t {edges_are_unsorted_multi_pass_filtered};
+// global mapping and filtering is done here because it is often faster and it
+// greatly simplifies handling of edge properties.
+enum edges_are_unsorted_multi_pass_global_t {edges_are_unsorted_multi_pass_global};
// A type (construct_inplace_from_sources_and_targets_t) and a value
// (construct_inplace_from_sources_and_targets) used to indicate that mutable
@@ -102,14 +108,14 @@ enum construct_inplace_from_sources_and_targets_t {construct_inplace_from_source
// (sequential and distributed).
enum construct_inplace_from_sources_and_targets_global_t {construct_inplace_from_sources_and_targets_global};
-// A type (edges_are_unsorted_for_distributed_graph_t) and a value
-// (edges_are_unsorted_for_distributed_graph) used to indicate that the edge
-// list passed into the CSR graph is not sorted by source vertex. The data is
-// also stored using global vertex indices, and must be filtered to choose only
-// local vertices. This constructor caches the edge information in memory, and
-// thus requires only a single pass over the input data. This constructor is
-// intended for internal use by the distributed CSR constructors.
-enum edges_are_unsorted_for_distributed_graph_t {edges_are_unsorted_for_distributed_graph};
+// A type (edges_are_unsorted_global_t) and a value (edges_are_unsorted_global)
+// used to indicate that the edge list passed into the CSR graph is not sorted
+// by source vertex. The data is also stored using global vertex indices, and
+// must be filtered to choose only local vertices. This constructor caches the
+// edge information in memory, and thus requires only a single pass over the
+// input data. This constructor is intended for internal use by the
+// distributed CSR constructors.
+enum edges_are_unsorted_global_t {edges_are_unsorted_global};
#endif // BOOST_GRAPH_USE_NEW_CSR_INTERFACE
@@ -128,7 +134,6 @@ enum edges_are_unsorted_for_distributed_graph_t {edges_are_unsorted_for_distribu
template
class csr_edge_descriptor;
-#ifdef BOOST_GRAPH_USE_NEW_CSR_INTERFACE
namespace detail {
template
size_t
@@ -157,8 +162,21 @@ namespace detail {
category;
return reserve_count_for_single_pass_helper(first, last, category());
}
-}
+
+#ifdef BOOST_GRAPH_USE_NEW_CSR_INTERFACE
+ template
+ struct default_construct_iterator: public boost::iterator_facade, T, boost::random_access_traversal_tag, const T&> {
+ typedef boost::iterator_facade, T, std::random_access_iterator_tag, const T&> base_type;
+ T saved_value;
+ const T& dereference() const {return saved_value;}
+ bool equal(default_construct_iterator i) const {return true;}
+ void increment() {}
+ void decrement() {}
+ void advance(typename base_type::difference_type) {}
+ typename base_type::difference_type distance_to(default_construct_iterator) const {return 0;}
+ };
#endif // BOOST_GRAPH_USE_NEW_CSR_INTERFACE
+}
/** Compressed sparse row graph.
*
@@ -166,8 +184,8 @@ namespace detail {
* specialize numeric_limits.
*/
template
@@ -179,6 +197,7 @@ class compressed_sparse_row_graph
EdgeIndex> >
{
+ public:
typedef detail::indexed_vertex_properties
inherited_vertex_properties;
@@ -280,25 +299,26 @@ class compressed_sparse_row_graph
#ifdef BOOST_GRAPH_USE_NEW_CSR_INTERFACE
// Rebuild graph from number of vertices and multi-pass unsorted list of
- // edges (filtered using edge_pred)
- template
+ // edges (filtered using edge_pred and mapped using global_to_local)
+ template
void
assign_unsorted_multi_pass_edges(MultiPassInputIterator edge_begin,
MultiPassInputIterator edge_end,
- vertices_size_type numverts,
+ vertices_size_type numlocalverts,
+ const GlobalToLocal& global_to_local,
const EdgePred& edge_pred) {
m_rowstart.clear();
- m_rowstart.resize(numverts + 1, 0);
+ m_rowstart.resize(numlocalverts + 1, 0);
// Put the degree of each vertex v into m_rowstart[v + 1]
for (MultiPassInputIterator i = edge_begin; i != edge_end; ++i)
if (edge_pred(*i))
- ++m_rowstart[i->first + 1];
+ ++m_rowstart[get(global_to_local, i->first) + 1];
// Compute the partial sum of the degrees to get the actual values of
// m_rowstart
EdgeIndex start_of_this_row = 0;
m_rowstart[0] = start_of_this_row;
- for (vertices_size_type i = 1; i <= numverts; ++i) {
+ for (vertices_size_type i = 1; i <= numlocalverts; ++i) {
start_of_this_row += m_rowstart[i];
m_rowstart[i] = start_of_this_row;
}
@@ -308,33 +328,35 @@ class compressed_sparse_row_graph
// into m_column. The index current_insert_positions[v] contains the next
// location to insert out edges for vertex v.
std::vector
- current_insert_positions(m_rowstart.begin(), m_rowstart.begin() + numverts);
+ current_insert_positions(m_rowstart.begin(), m_rowstart.begin() + numlocalverts);
for (; edge_begin != edge_end; ++edge_begin)
if (edge_pred(*edge_begin))
- m_column[current_insert_positions[edge_begin->first]++] = edge_begin->second;
+ m_column[current_insert_positions[get(global_to_local, edge_begin->first)]++] = edge_begin->second;
}
// Rebuild graph from number of vertices and multi-pass unsorted list of
- // edges and their properties (filtered using edge_pred)
- template
+ // edges and their properties (filtered using edge_pred and mapped using
+ // global_to_local)
+ template
void
assign_unsorted_multi_pass_edges(MultiPassInputIterator edge_begin,
MultiPassInputIterator edge_end,
EdgePropertyIterator ep_iter,
- vertices_size_type numverts,
+ vertices_size_type numlocalverts,
+ const GlobalToLocal& global_to_local,
const EdgePred& edge_pred) {
m_rowstart.clear();
- m_rowstart.resize(numverts + 1, 0);
+ m_rowstart.resize(numlocalverts + 1, 0);
// Put the degree of each vertex v into m_rowstart[v + 1]
for (MultiPassInputIterator i = edge_begin; i != edge_end; ++i)
if (edge_pred(*i))
- ++m_rowstart[i->first + 1];
+ ++m_rowstart[get(global_to_local, i->first) + 1];
// Compute the partial sum of the degrees to get the actual values of
// m_rowstart
EdgeIndex start_of_this_row = 0;
m_rowstart[0] = start_of_this_row;
- for (vertices_size_type i = 1; i <= numverts; ++i) {
+ for (vertices_size_type i = 1; i <= numlocalverts; ++i) {
start_of_this_row += m_rowstart[i];
m_rowstart[i] = start_of_this_row;
}
@@ -345,10 +367,10 @@ class compressed_sparse_row_graph
// into m_column. The index current_insert_positions[v] contains the next
// location to insert out edges for vertex v.
std::vector
- current_insert_positions(m_rowstart.begin(), m_rowstart.begin() + numverts);
+ current_insert_positions(m_rowstart.begin(), m_rowstart.begin() + numlocalverts);
for (; edge_begin != edge_end; ++edge_begin, ++ep_iter) {
if (edge_pred(*edge_begin)) {
- vertices_size_type source = edge_begin->first;
+ vertices_size_type source = get(global_to_local, edge_begin->first);
EdgeIndex insert_pos = current_insert_positions[source];
++current_insert_positions[source];
m_column[insert_pos] = edge_begin->second;
@@ -367,7 +389,7 @@ class compressed_sparse_row_graph
: inherited_vertex_properties(numverts), m_rowstart(),
m_column(0), m_property(prop)
{
- assign_unsorted_multi_pass_edges(edge_begin, edge_end, numverts, keep_all());
+ assign_unsorted_multi_pass_edges(edge_begin, edge_end, numverts, identity_property_map(), keep_all());
// Default-construct properties for edges
inherited_edge_properties::resize(m_column.size());
@@ -384,42 +406,113 @@ class compressed_sparse_row_graph
: inherited_vertex_properties(numverts), m_rowstart(),
m_column(0), m_property(prop)
{
- assign_unsorted_multi_pass_edges(edge_begin, edge_end, ep_iter, numverts, keep_all());
+ assign_unsorted_multi_pass_edges(edge_begin, edge_end, ep_iter, numverts, identity_property_map(), keep_all());
}
- // From number of vertices and unsorted list of edges, with filter
- template
- compressed_sparse_row_graph(edges_are_unsorted_multi_pass_filtered_t,
+ // From number of vertices and unsorted list of edges, with filter and
+ // global-to-local map
+ template
+ compressed_sparse_row_graph(edges_are_unsorted_multi_pass_global_t,
MultiPassInputIterator edge_begin,
MultiPassInputIterator edge_end,
- vertices_size_type numverts,
+ vertices_size_type numlocalverts,
+ const GlobalToLocal& global_to_local,
const EdgePred& edge_pred,
const GraphProperty& prop = GraphProperty())
- : inherited_vertex_properties(numverts), m_rowstart(),
+ : inherited_vertex_properties(numlocalverts), m_rowstart(),
m_column(0), m_property(prop)
{
- assign_unsorted_multi_pass_edges(edge_begin, edge_end, numverts, edge_pred);
+ assign_unsorted_multi_pass_edges(edge_begin, edge_end, numlocalverts, global_to_local, edge_pred);
// Default-construct properties for edges
inherited_edge_properties::resize(m_column.size());
}
- // From number of vertices and unsorted list of edges, plus edge properties, with filter
- template
- compressed_sparse_row_graph(edges_are_unsorted_multi_pass_filtered_t,
+ // From number of vertices and unsorted list of edges, plus edge properties,
+ // with filter and global-to-local map
+ template
+ compressed_sparse_row_graph(edges_are_unsorted_multi_pass_global_t,
MultiPassInputIterator edge_begin,
MultiPassInputIterator edge_end,
EdgePropertyIterator ep_iter,
- vertices_size_type numverts,
+ vertices_size_type numlocalverts,
+ const GlobalToLocal& global_to_local,
const EdgePred& edge_pred,
const GraphProperty& prop = GraphProperty())
- : inherited_vertex_properties(numverts), m_rowstart(),
+ : inherited_vertex_properties(numlocalverts), m_rowstart(),
m_column(0), m_property(prop)
{
- assign_unsorted_multi_pass_edges(edge_begin, edge_end, ep_iter, numverts, edge_pred);
+ assign_unsorted_multi_pass_edges(edge_begin, edge_end, ep_iter, numlocalverts, global_to_local, edge_pred);
}
#endif // BOOST_GRAPH_USE_NEW_CSR_INTERFACE
+ // Assign from number of vertices and sorted list of edges
+ template
+ void assign_from_sorted_edges(
+ InputIterator edge_begin, InputIterator edge_end,
+ const GlobalToLocal& global_to_local,
+ const EdgePred& edge_pred,
+ vertices_size_type numlocalverts,
+ edges_size_type numedges_or_zero) {
+ m_column.clear();
+ m_column.reserve(numedges_or_zero);
+ inherited_vertex_properties::resize(numlocalverts);
+ m_rowstart.resize(numlocalverts + 1);
+ EdgeIndex current_edge = 0;
+ Vertex current_vertex_plus_one = 1;
+ m_rowstart[0] = 0;
+ for (InputIterator ei = edge_begin; ei != edge_end; ++ei) {
+ if (!edge_pred(*ei)) continue;
+ Vertex src = get(global_to_local, ei->first);
+ Vertex tgt = ei->second;
+ for (; current_vertex_plus_one != src + 1; ++current_vertex_plus_one)
+ m_rowstart[current_vertex_plus_one] = current_edge;
+ m_column.push_back(tgt);
+ ++current_edge;
+ }
+
+ // The remaining vertices have no edges
+ for (; current_vertex_plus_one != numlocalverts + 1; ++current_vertex_plus_one)
+ m_rowstart[current_vertex_plus_one] = current_edge;
+
+ // Default-construct properties for edges
+ inherited_edge_properties::resize(m_column.size());
+ }
+
+ // Assign from number of vertices and sorted list of edges
+ template
+ void assign_from_sorted_edges(
+ InputIterator edge_begin, InputIterator edge_end,
+ EdgePropertyIterator ep_iter,
+ const GlobalToLocal& global_to_local,
+ const EdgePred& edge_pred,
+ vertices_size_type numlocalverts,
+ edges_size_type numedges_or_zero) {
+ m_column.clear();
+ m_column.reserve(numedges_or_zero);
+ inherited_edge_properties::clear();
+ inherited_edge_properties::reserve(numedges_or_zero);
+ inherited_vertex_properties::resize(numlocalverts);
+ m_rowstart.resize(numlocalverts + 1);
+ EdgeIndex current_edge = 0;
+ Vertex current_vertex_plus_one = 1;
+ m_rowstart[0] = 0;
+ for (InputIterator ei = edge_begin; ei != edge_end; ++ei, ++ep_iter) {
+ if (!edge_pred(*ei)) continue;
+ Vertex src = get(global_to_local, ei->first);
+ Vertex tgt = ei->second;
+ for (; current_vertex_plus_one != src + 1; ++current_vertex_plus_one)
+ m_rowstart[current_vertex_plus_one] = current_edge;
+ m_column.push_back(tgt);
+ inherited_edge_properties::push_back(*ep_iter);
+ ++current_edge;
+ }
+
+ // The remaining vertices have no edges
+ for (; current_vertex_plus_one != numlocalverts + 1; ++current_vertex_plus_one)
+ m_rowstart[current_vertex_plus_one] = current_edge;
+ }
+
#ifdef BOOST_GRAPH_USE_OLD_CSR_INTERFACE
// From number of vertices and sorted list of edges (deprecated
@@ -429,8 +522,7 @@ class compressed_sparse_row_graph
vertices_size_type numverts,
edges_size_type numedges = 0,
const GraphProperty& prop = GraphProperty())
- : inherited_vertex_properties(numverts), m_rowstart(numverts + 1),
- m_column(0), m_property(prop), m_last_source(numverts)
+ : m_property(prop), m_last_source(numverts)
{
// Reserving storage in advance can save us lots of time and
// memory, but it can only be done if we have forward iterators or
@@ -438,29 +530,9 @@ class compressed_sparse_row_graph
if (numedges == 0) {
typedef typename std::iterator_traits::iterator_category
category;
- maybe_reserve_edge_list_storage(edge_begin, edge_end, category());
- } else {
- m_column.reserve(numedges);
+ numedges = detail::reserve_count_for_single_pass(edge_begin, edge_end);
}
-
- EdgeIndex current_edge = 0;
- Vertex current_vertex_plus_one = 1;
- m_rowstart[0] = 0;
- for (InputIterator ei = edge_begin; ei != edge_end; ++ei) {
- Vertex src = ei->first;
- Vertex tgt = ei->second;
- for (; current_vertex_plus_one != src + 1; ++current_vertex_plus_one)
- m_rowstart[current_vertex_plus_one] = current_edge;
- m_column.push_back(tgt);
- ++current_edge;
- }
-
- // The remaining vertices have no edges
- for (; current_vertex_plus_one != numverts + 1; ++current_vertex_plus_one)
- m_rowstart[current_vertex_plus_one] = current_edge;
-
- // Default-construct properties for edges
- inherited_edge_properties::resize(m_column.size());
+ assign_from_sorted_edges(edge_begin, edge_end, identity_property_map(), keep_all(), numverts, numedges);
}
// From number of vertices and sorted list of edges (deprecated
@@ -471,8 +543,7 @@ class compressed_sparse_row_graph
vertices_size_type numverts,
edges_size_type numedges = 0,
const GraphProperty& prop = GraphProperty())
- : inherited_vertex_properties(numverts), m_rowstart(numverts + 1),
- m_column(0), m_property(prop), m_last_source(numverts)
+ : m_property(prop), m_last_source(numverts)
{
// Reserving storage in advance can save us lots of time and
// memory, but it can only be done if we have forward iterators or
@@ -480,27 +551,9 @@ class compressed_sparse_row_graph
if (numedges == 0) {
typedef typename std::iterator_traits::iterator_category
category;
- maybe_reserve_edge_list_storage(edge_begin, edge_end, category());
- } else {
- m_column.reserve(numedges);
+ numedges = detail::reserve_count_for_single_pass(edge_begin, edge_end);
}
-
- EdgeIndex current_edge = 0;
- Vertex current_vertex_plus_one = 1;
- m_rowstart[0] = 0;
- for (InputIterator ei = edge_begin; ei != edge_end; ++ei, ++ep_iter) {
- Vertex src = ei->first;
- Vertex tgt = ei->second;
- for (; current_vertex_plus_one != src + 1; ++current_vertex_plus_one)
- m_rowstart[current_vertex_plus_one] = current_edge;
- m_column.push_back(tgt);
- inherited_edge_properties::push_back(*ep_iter);
- ++current_edge;
- }
-
- // The remaining vertices have no edges
- for (; current_vertex_plus_one != numverts + 1; ++current_vertex_plus_one)
- m_rowstart[current_vertex_plus_one] = current_edge;
+ assign_from_sorted_edges(edge_begin, edge_end, ep_iter, identity_property_map(), keep_all(), numverts, numedges);
}
#endif // BOOST_GRAPH_USE_OLD_CSR_INTERFACE
@@ -512,8 +565,7 @@ class compressed_sparse_row_graph
vertices_size_type numverts,
edges_size_type numedges = 0,
const GraphProperty& prop = GraphProperty())
- : inherited_vertex_properties(numverts), m_rowstart(numverts + 1),
- m_column(0), m_property(prop)
+ : m_property(prop)
#ifdef BOOST_GRAPH_USE_OLD_CSR_INTERFACE
, m_last_source(numverts)
#endif // BOOST_GRAPH_USE_OLD_CSR_INTERFACE
@@ -524,29 +576,9 @@ class compressed_sparse_row_graph
if (numedges == 0) {
typedef typename std::iterator_traits::iterator_category
category;
- maybe_reserve_edge_list_storage(edge_begin, edge_end, category());
- } else {
- m_column.reserve(numedges);
+ numedges = detail::reserve_count_for_single_pass(edge_begin, edge_end);
}
-
- EdgeIndex current_edge = 0;
- Vertex current_vertex_plus_one = 1;
- m_rowstart[0] = 0;
- for (InputIterator ei = edge_begin; ei != edge_end; ++ei) {
- Vertex src = ei->first;
- Vertex tgt = ei->second;
- for (; current_vertex_plus_one != src + 1; ++current_vertex_plus_one)
- m_rowstart[current_vertex_plus_one] = current_edge;
- m_column.push_back(tgt);
- ++current_edge;
- }
-
- // The remaining vertices have no edges
- for (; current_vertex_plus_one != numverts + 1; ++current_vertex_plus_one)
- m_rowstart[current_vertex_plus_one] = current_edge;
-
- // Default-construct properties for edges
- inherited_edge_properties::resize(m_column.size());
+ assign_from_sorted_edges(edge_begin, edge_end, identity_property_map(), keep_all(), numverts, numedges);
}
// From number of vertices and sorted list of edges (new interface)
@@ -557,8 +589,7 @@ class compressed_sparse_row_graph
vertices_size_type numverts,
edges_size_type numedges = 0,
const GraphProperty& prop = GraphProperty())
- : inherited_vertex_properties(numverts), m_rowstart(numverts + 1),
- m_column(0), m_property(prop)
+ : m_property(prop)
#ifdef BOOST_GRAPH_USE_OLD_CSR_INTERFACE
, m_last_source(numverts)
#endif // BOOST_GRAPH_USE_OLD_CSR_INTERFACE
@@ -569,30 +600,45 @@ class compressed_sparse_row_graph
if (numedges == 0) {
typedef typename std::iterator_traits::iterator_category
category;
- maybe_reserve_edge_list_storage(edge_begin, edge_end, category());
- } else {
- m_column.reserve(numedges);
+ numedges = detail::reserve_count_for_single_pass(edge_begin, edge_end);
}
-
- EdgeIndex current_edge = 0;
- Vertex current_vertex_plus_one = 1;
- m_rowstart[0] = 0;
- for (InputIterator ei = edge_begin; ei != edge_end; ++ei, ++ep_iter) {
- Vertex src = ei->first;
- Vertex tgt = ei->second;
- for (; current_vertex_plus_one != src + 1; ++current_vertex_plus_one)
- m_rowstart[current_vertex_plus_one] = current_edge;
- m_column.push_back(tgt);
- inherited_edge_properties::push_back(*ep_iter);
- ++current_edge;
- }
-
- // The remaining vertices have no edges
- for (; current_vertex_plus_one != numverts + 1; ++current_vertex_plus_one)
- m_rowstart[current_vertex_plus_one] = current_edge;
+ assign_from_sorted_edges(edge_begin, edge_end, ep_iter, identity_property_map(), keep_all(), numverts, numedges);
}
#ifdef BOOST_GRAPH_USE_NEW_CSR_INTERFACE
+ // From number of vertices and sorted list of edges, filtered and global (new interface)
+ template
+ compressed_sparse_row_graph(edges_are_sorted_global_t,
+ InputIterator edge_begin, InputIterator edge_end,
+ const GlobalToLocal& global_to_local,
+ const EdgePred& edge_pred,
+ vertices_size_type numverts,
+ const GraphProperty& prop = GraphProperty())
+ : m_property(prop)
+#ifdef BOOST_GRAPH_USE_OLD_CSR_INTERFACE
+ , m_last_source(numverts)
+#endif // BOOST_GRAPH_USE_OLD_CSR_INTERFACE
+ {
+ assign_from_sorted_edges(edge_begin, edge_end, global_to_local, edge_pred, numverts, 0);
+ }
+
+ // From number of vertices and sorted list of edges (new interface)
+ template
+ compressed_sparse_row_graph(edges_are_sorted_global_t,
+ InputIterator edge_begin, InputIterator edge_end,
+ EdgePropertyIterator ep_iter,
+ const GlobalToLocal& global_to_local,
+ const EdgePred& edge_pred,
+ vertices_size_type numverts,
+ const GraphProperty& prop = GraphProperty())
+ : m_property(prop)
+#ifdef BOOST_GRAPH_USE_OLD_CSR_INTERFACE
+ , m_last_source(numverts)
+#endif // BOOST_GRAPH_USE_OLD_CSR_INTERFACE
+ {
+ assign_from_sorted_edges(edge_begin, edge_end, ep_iter, global_to_local, edge_pred, numverts, 0);
+ }
+
// From number of vertices and mutable vectors of sources and targets;
// vectors are returned with unspecified contents but are guaranteed not to
// share storage with the constructed graph.
@@ -631,7 +677,7 @@ class compressed_sparse_row_graph
compressed_sparse_row_graph(construct_inplace_from_sources_and_targets_t,
std::vector& sources,
std::vector& targets,
- std::vector& edge_props,
+ std::vector& edge_props,
vertices_size_type numverts,
const GraphProperty& prop = GraphProperty())
: inherited_vertex_properties(numverts), m_rowstart(),
@@ -649,7 +695,7 @@ class compressed_sparse_row_graph
compressed_sparse_row_graph(construct_inplace_from_sources_and_targets_global_t,
std::vector& sources,
std::vector& targets,
- std::vector& edge_props,
+ std::vector& edge_props,
vertices_size_type numlocalverts,
GlobalToLocal global_to_local,
const GraphProperty& prop = GraphProperty())
@@ -694,7 +740,7 @@ class compressed_sparse_row_graph
m_column(), m_property(prop)
{
std::vector sources, targets;
- std::vector edge_props;
+ std::vector edge_props;
size_t reserve_size
= detail::reserve_count_for_single_pass(edge_begin, edge_end);
sources.reserve(reserve_size);
@@ -712,7 +758,7 @@ class compressed_sparse_row_graph
// cached in coordinate form before creating the actual graph. Edges are
// filtered and transformed for use in a distributed graph.
template
- compressed_sparse_row_graph(edges_are_unsorted_for_distributed_graph_t,
+ compressed_sparse_row_graph(edges_are_unsorted_global_t,
InputIterator edge_begin, InputIterator edge_end,
vertices_size_type numlocalverts,
GlobalToLocal global_to_local,
@@ -737,7 +783,7 @@ class compressed_sparse_row_graph
// use in a distributed graph.
template
- compressed_sparse_row_graph(edges_are_unsorted_for_distributed_graph_t,
+ compressed_sparse_row_graph(edges_are_unsorted_global_t,
InputIterator edge_begin, InputIterator edge_end,
EdgePropertyIterator ep_iter,
vertices_size_type numlocalverts,
@@ -748,7 +794,7 @@ class compressed_sparse_row_graph
m_column(), m_property(prop)
{
std::vector sources, targets;
- std::vector edge_props;
+ std::vector edge_props;
for (; edge_begin != edge_end; ++edge_begin, ++ep_iter) {
if (edge_pred(*edge_begin)) {
sources.push_back(edge_begin->first);
@@ -776,6 +822,7 @@ class compressed_sparse_row_graph
m_rowstart.clear();
m_rowstart.resize(numverts + 1);
for (size_t i = 0; i < numedges; ++i) {
+ assert(get(global_to_local, sources[i]) < numverts);
++m_rowstart[get(global_to_local, sources[i]) + 1];
}
// 2. Do a prefix sum on those to get starting positions of each row
@@ -809,7 +856,7 @@ class compressed_sparse_row_graph
template
void assign_sources_and_targets_global(std::vector& sources,
std::vector& targets,
- std::vector& edge_props,
+ std::vector& edge_props,
vertices_size_type numverts,
GlobalToLocal global_to_local) {
assert (sources.size() == targets.size());
@@ -933,6 +980,100 @@ class compressed_sparse_row_graph
assign(g, get(vertex_index, g), num_vertices(g), num_edges(g));
}
+#ifdef BOOST_GRAPH_USE_NEW_CSR_INTERFACE
+ // Add edges from a sorted (smallest sources first) range of pairs and edge
+ // properties
+ template
+ void
+ add_edges_sorted_internal(
+ BidirectionalIteratorOrig first_sorted,
+ BidirectionalIteratorOrig last_sorted,
+ EPIterOrig ep_iter_sorted) {
+ typedef boost::reverse_iterator BidirectionalIterator;
+ typedef boost::reverse_iterator EPIter;
+ // Flip sequence
+ BidirectionalIterator first(last_sorted);
+ BidirectionalIterator last(first_sorted);
+ typedef compressed_sparse_row_graph Graph;
+ typedef typename boost::graph_traits::vertex_descriptor vertex_t;
+ typedef typename boost::graph_traits::vertices_size_type vertex_num;
+ typedef typename boost::graph_traits::edges_size_type edge_num;
+ edge_num new_edge_count = std::distance(first, last);
+ EPIter ep_iter(ep_iter_sorted);
+ std::advance(ep_iter, -(std::ptrdiff_t)new_edge_count);
+ edge_num edges_added_before_i = new_edge_count; // Count increment to add to rowstarts
+ m_column.resize(m_column.size() + new_edge_count);
+ inherited_edge_properties::resize(inherited_edge_properties::size() + new_edge_count);
+ BidirectionalIterator current_new_edge = first, prev_new_edge = first;
+ EPIter current_new_edge_prop = ep_iter;
+ for (vertex_num i_plus_1 = num_vertices(*this); i_plus_1 > 0; --i_plus_1) {
+ vertex_num i = i_plus_1 - 1;
+ prev_new_edge = current_new_edge;
+ // edges_added_to_this_vertex = #mbrs of new_edges with first == i
+ edge_num edges_added_to_this_vertex = 0;
+ while (current_new_edge != last) {
+ if (current_new_edge->first != i) break;
+ ++current_new_edge;
+ ++current_new_edge_prop;
+ ++edges_added_to_this_vertex;
+ }
+ edges_added_before_i -= edges_added_to_this_vertex;
+ // Invariant: edges_added_before_i = #mbrs of new_edges with first < i
+ edge_num old_rowstart = m_rowstart[i];
+ edge_num new_rowstart = m_rowstart[i] + edges_added_before_i;
+ edge_num old_degree = m_rowstart[i + 1] - m_rowstart[i];
+ edge_num new_degree = old_degree + edges_added_to_this_vertex;
+ // Move old edges forward (by #new_edges before this i) to make room
+ // new_rowstart > old_rowstart, so use copy_backwards
+ if (old_rowstart != new_rowstart) {
+ std::copy_backward(m_column.begin() + old_rowstart,
+ m_column.begin() + old_rowstart + old_degree,
+ m_column.begin() + new_rowstart + old_degree);
+ inherited_edge_properties::move_range(old_rowstart, old_rowstart + old_degree, new_rowstart);
+ }
+ // Add new edges (reversed because current_new_edge is a
+ // const_reverse_iterator)
+ BidirectionalIterator temp = current_new_edge;
+ EPIter temp_prop = current_new_edge_prop;
+ for (; temp != prev_new_edge; ++old_degree) {
+ --temp;
+ m_column[new_rowstart + old_degree] = temp->second;
+ inherited_edge_properties::write_by_index(new_rowstart + old_degree, *temp_prop);
+ }
+ m_rowstart[i + 1] = new_rowstart + new_degree;
+ if (edges_added_before_i == 0) break; // No more edges inserted before this point
+ // m_rowstart[i] will be fixed up on the next iteration (to avoid
+ // changing the degree of vertex i - 1); the last iteration never changes
+ // it (either because of the condition of the break or because
+ // m_rowstart[0] is always 0)
+ }
+ }
+
+ // Add edges from a sorted (smallest sources first) range of pairs
+ template
+ void
+ add_edges_sorted_internal(
+ BidirectionalIteratorOrig first_sorted,
+ BidirectionalIteratorOrig last_sorted) {
+ add_edges_sorted_internal(first_sorted, last_sorted, detail::default_construct_iterator());
+ }
+
+ // Add edges from a range of (source, target) pairs that are unsorted
+ template
+ inline void
+ add_edges_internal(InputIterator first, InputIterator last) {
+ typedef compressed_sparse_row_graph Graph;
+ typedef typename boost::graph_traits::vertex_descriptor vertex_t;
+ typedef typename boost::graph_traits::vertices_size_type vertex_num;
+ typedef typename boost::graph_traits::edges_size_type edge_num;
+ typedef std::vector > edge_vector_t;
+ edge_vector_t new_edges(first, last);
+ if (new_edges.empty()) return;
+ std::sort(new_edges.begin(), new_edges.end());
+ add_edges_sorted_internal(new_edges.begin(), new_edges.end());
+ }
+#endif // BOOST_GRAPH_USE_NEW_CSR_INTERFACE
+
using inherited_vertex_properties::operator[];
using inherited_edge_properties::operator[];
@@ -1044,62 +1185,35 @@ add_edge(Vertex src, Vertex tgt,
#endif // BOOST_GRAPH_USE_OLD_CSR_INTERFACE
#ifdef BOOST_GRAPH_USE_NEW_CSR_INTERFACE
-// Add edges from a range of (source, target) pairs that are unsorted
-template
-inline void
-add_edges(InputIterator first, InputIterator last, BOOST_CSR_GRAPH_TYPE& g) {
- typedef BOOST_CSR_GRAPH_TYPE Graph;
- typedef typename boost::graph_traits::vertex_descriptor vertex_t;
- typedef typename boost::graph_traits::vertices_size_type vertex_num;
- typedef typename boost::graph_traits::edges_size_type edge_num;
- typedef std::vector > edge_vector_t;
- edge_vector_t new_edges(first, last);
- if (new_edges.empty()) return;
- std::sort(new_edges.begin(), new_edges.end());
- edge_num edges_added_before_i = new_edges.size(); // Count increment to add to rowstarts
- g.m_column.resize(g.m_column.size() + new_edges.size());
- typename edge_vector_t::const_reverse_iterator
- current_new_edge = new_edges.rbegin(),
- prev_new_edge = new_edges.rbegin();
- for (vertex_num i_plus_1 = num_vertices(g); i_plus_1 > 0; --i_plus_1) {
- vertex_num i = i_plus_1 - 1;
- prev_new_edge = current_new_edge;
- // edges_added_to_this_vertex = #mbrs of new_edges with first == i
- edge_num edges_added_to_this_vertex = 0;
- while (current_new_edge !=
- (typename edge_vector_t::const_reverse_iterator)new_edges.rend()) {
- if (current_new_edge->first != i) break;
- ++current_new_edge;
- ++edges_added_to_this_vertex;
- }
- edges_added_before_i -= edges_added_to_this_vertex;
- // Invariant: edges_added_before_i = #mbrs of new_edges with first < i
- edge_num old_rowstart = g.m_rowstart[i];
- edge_num new_rowstart = g.m_rowstart[i] + edges_added_before_i;
- edge_num old_degree = g.m_rowstart[i + 1] - g.m_rowstart[i];
- edge_num new_degree = old_degree + edges_added_to_this_vertex;
- // Move old edges forward (by #new_edges before this i) to make room
- // new_rowstart > old_rowstart, so use copy_backwards
- if (old_rowstart != new_rowstart) {
- std::copy_backward(g.m_column.begin() + old_rowstart,
- g.m_column.begin() + old_rowstart + old_degree,
- g.m_column.begin() + new_rowstart + old_degree);
- }
- // Add new edges (reversed because current_new_edge is a
- // const_reverse_iterator)
- typename edge_vector_t::const_reverse_iterator temp = current_new_edge;
- for (; temp != prev_new_edge; ++old_degree) {
- --temp;
- g.m_column[new_rowstart + old_degree] = temp->second;
- }
- g.m_rowstart[i + 1] = new_rowstart + new_degree;
- if (edges_added_before_i == 0) break; // No more edges inserted before this point
- // g.m_rowstart[i] will be fixed up on the next iteration (to avoid
- // changing the degree of vertex i - 1); the last iteration never changes
- // it (either because of the condition of the break or because
- // g.m_rowstart[0] is always 0)
+ // Add edges from a sorted (smallest sources first) range of pairs and edge
+ // properties
+ template
+ void
+ add_edges_sorted(
+ BidirectionalIteratorOrig first_sorted,
+ BidirectionalIteratorOrig last_sorted,
+ EPIterOrig ep_iter_sorted,
+ BOOST_CSR_GRAPH_TYPE& g) {
+ g.add_edges_sorted_internal(first_sorted, last_sorted, ep_iter_sorted);
+ }
+
+ // Add edges from a sorted (smallest sources first) range of pairs
+ template
+ void
+ add_edges_sorted(
+ BidirectionalIteratorOrig first_sorted,
+ BidirectionalIteratorOrig last_sorted,
+ BOOST_CSR_GRAPH_TYPE& g) {
+ g.add_edges_sorted_internal(first_sorted, last_sorted);
+ }
+
+ // Add edges from a range of (source, target) pairs that are unsorted
+ template
+ inline void
+ add_edges(InputIterator first, InputIterator last, BOOST_CSR_GRAPH_TYPE& g) {
+ g.add_edges_internal(first, last);
}
-}
#endif // BOOST_GRAPH_USE_NEW_CSR_INTERFACE
// From VertexListGraph
diff --git a/include/boost/graph/detail/indexed_properties.hpp b/include/boost/graph/detail/indexed_properties.hpp
index 57caea50..8fbd9e93 100644
--- a/include/boost/graph/detail/indexed_properties.hpp
+++ b/include/boost/graph/detail/indexed_properties.hpp
@@ -50,6 +50,12 @@ protected:
indexed_vertex_properties(std::size_t n) : m_vertex_properties(n) { }
public:
+ // Clear the properties vector
+ void clear()
+ {
+ m_vertex_properties.clear();
+ }
+
// Resize the properties vector
void resize(std::size_t n)
{
@@ -101,6 +107,7 @@ class indexed_vertex_properties
indexed_vertex_properties(std::size_t) { }
public:
+ void clear() { }
void resize(std::size_t) { }
void reserve(std::size_t) { }
};
@@ -127,6 +134,18 @@ protected:
// Initialize with n default-constructed property values
indexed_edge_properties(std::size_t n) : m_edge_properties(n) { }
+ // Get the size of the properties vector
+ std::size_t size() const
+ {
+ return m_edge_properties.size();
+ }
+
+ // Clear the properties vector
+ void clear()
+ {
+ m_edge_properties.clear();
+ }
+
// Resize the properties vector
void resize(std::size_t n)
{
@@ -152,6 +171,14 @@ protected:
m_edge_properties.push_back(prop);
}
+ // Move range of properties backwards
+ void move_range(std::size_t src_begin, std::size_t src_end, std::size_t dest_begin) {
+ std::copy_backward(
+ m_edge_properties.begin() + src_begin,
+ m_edge_properties.begin() + src_end,
+ m_edge_properties.begin() + dest_begin + (src_end - src_begin));
+ }
+
private:
// Access to the derived object
Derived& derived() { return *static_cast(this); }
@@ -174,16 +201,21 @@ class indexed_edge_properties
typedef void* edge_push_back_type;
secret operator[](secret) { return secret(); }
+ void write_by_index(std::size_t idx, const no_property& prop) {}
protected:
// All operations do nothing.
indexed_edge_properties() { }
indexed_edge_properties(std::size_t) { }
+ std::size_t size() const {return 0;}
+ void clear() { }
void resize(std::size_t) { }
void reserve(std::size_t) { }
public:
void push_back(const edge_push_back_type&) { }
+ void move_range(std::size_t src_begin, std::size_t src_end, std::size_t dest_begin) {}
+
};
}
diff --git a/include/boost/graph/filtered_graph.hpp b/include/boost/graph/filtered_graph.hpp
index 2a30a712..94b634df 100644
--- a/include/boost/graph/filtered_graph.hpp
+++ b/include/boost/graph/filtered_graph.hpp
@@ -500,6 +500,23 @@ namespace boost {
return Filter(g, keep_all(), p);
}
+ // Filter that uses a property map whose value_type is a boolean
+ template
+ struct property_map_filter {
+
+ property_map_filter() { }
+
+ property_map_filter(const PropertyMap& property_map) :
+ m_property_map(property_map) { }
+
+ template
+ bool operator()(const Key& key) const {
+ return (get(m_property_map, key));
+ }
+
+ private :
+ PropertyMap m_property_map;
+ };
} // namespace boost
diff --git a/include/boost/graph/graph_utility.hpp b/include/boost/graph/graph_utility.hpp
index 2b65177e..9976074c 100644
--- a/include/boost/graph/graph_utility.hpp
+++ b/include/boost/graph/graph_utility.hpp
@@ -468,6 +468,43 @@ namespace boost {
} // namespace graph
+ #include
+
+ template
+ void copy_vertex_property(PropertyIn p_in, PropertyOut p_out, Graph& g)
+ {
+ BGL_FORALL_VERTICES_T(u, g, Graph)
+ put(p_out, u, get(p_in, g));
+ }
+
+ template
+ void copy_edge_property(PropertyIn p_in, PropertyOut p_out, Graph& g)
+ {
+ BGL_FORALL_EDGES_T(e, g, Graph)
+ put(p_out, e, get(p_in, g));
+ }
+
+ // Return true if property_map1 and property_map2 differ
+ // for any of the vertices in graph.
+ template
+ bool are_property_maps_different
+ (const PropertyMapFirst property_map1,
+ const PropertyMapSecond property_map2,
+ const Graph& graph) {
+
+ BGL_FORALL_VERTICES_T(vertex, graph, Graph) {
+ if (get(property_map1, vertex) !=
+ get(property_map2, vertex)) {
+
+ return (true);
+ }
+ }
+
+ return (false);
+ }
+
} /* namespace boost */
#endif /* BOOST_GRAPH_UTILITY_HPP*/
diff --git a/include/boost/graph/mcgregor_common_subgraphs.hpp b/include/boost/graph/mcgregor_common_subgraphs.hpp
new file mode 100644
index 00000000..25db09ca
--- /dev/null
+++ b/include/boost/graph/mcgregor_common_subgraphs.hpp
@@ -0,0 +1,1142 @@
+//=======================================================================
+// Copyright 2009 Trustees of Indiana University.
+// Authors: Michael Hansen, Andrew Lumsdaine
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//=======================================================================
+
+#ifndef BOOST_GRAPH_MCGREGOR_COMMON_SUBGRAPHS_HPP
+#define BOOST_GRAPH_MCGREGOR_COMMON_SUBGRAPHS_HPP
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace boost {
+
+ namespace detail {
+
+ // Traits associated with common subgraphs, used mainly to keep a
+ // consistent type for the correspondence maps.
+ template
+ struct mcgregor_common_subgraph_traits {
+ typedef typename graph_traits::vertex_descriptor vertex_first_type;
+ typedef typename graph_traits::vertex_descriptor vertex_second_type;
+
+ typedef shared_array_property_map
+ correspondence_map_first_to_second_type;
+
+ typedef shared_array_property_map
+ correspondence_map_second_to_first_type;
+ };
+
+ } // namespace detail
+
+ // ==========================================================================
+
+ // Binary function object that returns true if the values for item1
+ // in property_map1 and item2 in property_map2 are equivalent.
+ template
+ struct property_map_equivalent {
+
+ property_map_equivalent(const PropertyMapFirst property_map1,
+ const PropertyMapSecond property_map2) :
+ m_property_map1(property_map1),
+ m_property_map2(property_map2) { }
+
+ template
+ bool operator()(const ItemFirst item1, const ItemSecond item2) {
+ return (get(m_property_map1, item1) == get(m_property_map2, item2));
+ }
+
+ private:
+ const PropertyMapFirst m_property_map1;
+ const PropertyMapSecond m_property_map2;
+ };
+
+ // Returns a property_map_equivalent object that compares the values
+ // of property_map1 and property_map2.
+ template
+ property_map_equivalent
+ make_property_map_equivalent
+ (const PropertyMapFirst property_map1,
+ const PropertyMapSecond property_map2) {
+
+ return (property_map_equivalent
+ (property_map1, property_map2));
+ }
+
+ // Binary function object that always returns true. Used when
+ // vertices or edges are always equivalent (i.e. have no labels).
+ struct always_equivalent {
+
+ template
+ bool operator()(const ItemFirst& item1, const ItemSecond& item2) {
+ return (true);
+ }
+ };
+
+ // ==========================================================================
+
+ namespace detail {
+
+ // Return true if new_vertex1 and new_vertex2 can extend the
+ // subgraph represented by correspondence_map_1_to_2 and
+ // correspondence_map_2_to_1. The vertices_equivalent and
+ // edges_equivalent predicates are used to test vertex and edge
+ // equivalency between the two graphs.
+ template
+ bool can_extend_graph
+ (const GraphFirst& graph1,
+ const GraphSecond& graph2,
+ CorrespondenceMapFirstToSecond correspondence_map_1_to_2,
+ CorrespondenceMapSecondToFirst correspondence_map_2_to_1,
+ typename graph_traits::vertices_size_type subgraph_size,
+ typename graph_traits::vertex_descriptor new_vertex1,
+ typename graph_traits::vertex_descriptor new_vertex2,
+ EdgeEquivalencePredicate edges_equivalent,
+ VertexEquivalencePredicate vertices_equivalent,
+ bool only_connected_subgraphs)
+ {
+ typedef typename graph_traits::vertex_descriptor VertexFirst;
+ typedef typename graph_traits::vertex_descriptor VertexSecond;
+
+ typedef typename graph_traits::edge_descriptor EdgeFirst;
+ typedef typename graph_traits::edge_descriptor EdgeSecond;
+
+ // Check vertex equality
+ if (!vertices_equivalent(new_vertex1, new_vertex2)) {
+ return (false);
+ }
+
+ // Vertices match and graph is empty, so we can extend the subgraph
+ if (subgraph_size == 0) {
+ return (true);
+ }
+
+ bool has_one_edge = false;
+
+ // Verify edges with existing sub-graph
+ BGL_FORALL_VERTICES_T(existing_vertex1, graph1, GraphFirst) {
+
+ VertexSecond existing_vertex2 = get(correspondence_map_1_to_2, existing_vertex1);
+
+ // Skip unassociated vertices
+ if (existing_vertex2 == graph_traits::null_vertex()) {
+ continue;
+ }
+
+ // NOTE: This will not work with parallel edges, since the
+ // first matching edge is always chosen.
+ EdgeFirst edge_to_new1, edge_from_new1;
+ bool edge_to_new_exists1 = false, edge_from_new_exists1 = false;
+
+ EdgeSecond edge_to_new2, edge_from_new2;
+ bool edge_to_new_exists2 = false, edge_from_new_exists2 = false;
+
+ // Search for edge from existing to new vertex (graph1)
+ BGL_FORALL_OUTEDGES_T(existing_vertex1, edge1, graph1, GraphFirst) {
+ if (target(edge1, graph1) == new_vertex1) {
+ edge_to_new1 = edge1;
+ edge_to_new_exists1 = true;
+ break;
+ }
+ }
+
+ // Search for edge from existing to new vertex (graph2)
+ BGL_FORALL_OUTEDGES_T(existing_vertex2, edge2, graph2, GraphSecond) {
+ if (target(edge2, graph2) == new_vertex2) {
+ edge_to_new2 = edge2;
+ edge_to_new_exists2 = true;
+ break;
+ }
+ }
+
+ // Make sure edges from existing to new vertices are equivalent
+ if ((edge_to_new_exists1 != edge_to_new_exists2) ||
+ ((edge_to_new_exists1 && edge_to_new_exists2) &&
+ !edges_equivalent(edge_to_new1, edge_to_new2))) {
+
+ return (false);
+ }
+
+ bool is_undirected1 = is_undirected(graph1),
+ is_undirected2 = is_undirected(graph2);
+
+ if (is_undirected1 && is_undirected2) {
+
+ // Edge in both graphs exists and both graphs are undirected
+ if (edge_to_new_exists1 && edge_to_new_exists2) {
+ has_one_edge = true;
+ }
+
+ continue;
+ }
+ else {
+
+ if (!is_undirected1) {
+
+ // Search for edge from new to existing vertex (graph1)
+ BGL_FORALL_OUTEDGES_T(new_vertex1, edge1, graph1, GraphFirst) {
+ if (target(edge1, graph1) == existing_vertex1) {
+ edge_from_new1 = edge1;
+ edge_from_new_exists1 = true;
+ break;
+ }
+ }
+ }
+
+ if (!is_undirected2) {
+
+ // Search for edge from new to existing vertex (graph2)
+ BGL_FORALL_OUTEDGES_T(new_vertex2, edge2, graph2, GraphSecond) {
+ if (target(edge2, graph2) == existing_vertex2) {
+ edge_from_new2 = edge2;
+ edge_from_new_exists2 = true;
+ break;
+ }
+ }
+ }
+
+ // Make sure edges from new to existing vertices are equivalent
+ if ((edge_from_new_exists1 != edge_from_new_exists2) ||
+ ((edge_from_new_exists1 && edge_from_new_exists2) &&
+ !edges_equivalent(edge_from_new1, edge_from_new2))) {
+
+ return (false);
+ }
+
+ if ((edge_from_new_exists1 && edge_from_new_exists2) ||
+ (edge_to_new_exists1 && edge_to_new_exists2)) {
+ has_one_edge = true;
+ }
+
+ } // else
+
+ } // BGL_FORALL_VERTICES_T
+
+ // Make sure new vertices are connected to the existing subgraph
+ if (only_connected_subgraphs && !has_one_edge) {
+ return (false);
+ }
+
+ return (true);
+ }
+
+ // Recursive method that does a depth-first search in the space of
+ // potential subgraphs. At each level, every new vertex pair from
+ // both graphs is tested to see if it can extend the current
+ // subgraph. If so, the subgraph is output to subgraph_callback
+ // in the form of two correspondence maps (one for each graph).
+ // Returning false from subgraph_callback will terminate the
+ // search. Function returns true if the entire search space was
+ // explored.
+ template
+ bool mcgregor_common_subgraphs_internal
+ (const GraphFirst& graph1,
+ const GraphSecond& graph2,
+ const VertexIndexMapFirst& vindex_map1,
+ const VertexIndexMapSecond& vindex_map2,
+ CorrespondenceMapFirstToSecond correspondence_map_1_to_2,
+ CorrespondenceMapSecondToFirst correspondence_map_2_to_1,
+ VertexStackFirst& vertex_stack1,
+ EdgeEquivalencePredicate edges_equivalent,
+ VertexEquivalencePredicate vertices_equivalent,
+ bool only_connected_subgraphs,
+ SubGraphInternalCallback subgraph_callback)
+ {
+ typedef typename graph_traits::vertex_descriptor VertexFirst;
+ typedef typename graph_traits::vertex_descriptor VertexSecond;
+ typedef typename graph_traits::vertices_size_type VertexSizeFirst;
+
+ // Get iterators for vertices from both graphs
+ typename graph_traits::vertex_iterator
+ vertex1_iter, vertex1_end;
+
+ typename graph_traits::vertex_iterator
+ vertex2_begin, vertex2_end, vertex2_iter;
+
+ tie(vertex1_iter, vertex1_end) = vertices(graph1);
+ tie(vertex2_begin, vertex2_end) = vertices(graph2);
+ vertex2_iter = vertex2_begin;
+
+ // Iterate until all vertices have been visited
+ BGL_FORALL_VERTICES_T(new_vertex1, graph1, GraphFirst) {
+
+ VertexSecond existing_vertex2 = get(correspondence_map_1_to_2, new_vertex1);
+
+ // Skip already matched vertices in first graph
+ if (existing_vertex2 != graph_traits::null_vertex()) {
+ continue;
+ }
+
+ BGL_FORALL_VERTICES_T(new_vertex2, graph2, GraphSecond) {
+
+ VertexFirst existing_vertex1 = get(correspondence_map_2_to_1, new_vertex2);
+
+ // Skip already matched vertices in second graph
+ if (existing_vertex1 != graph_traits::null_vertex()) {
+ continue;
+ }
+
+ // Check if current sub-graph can be extended with the matched vertex pair
+ if (can_extend_graph(graph1, graph2,
+ correspondence_map_1_to_2, correspondence_map_2_to_1,
+ (VertexSizeFirst)vertex_stack1.size(),
+ new_vertex1, new_vertex2,
+ edges_equivalent, vertices_equivalent,
+ only_connected_subgraphs)) {
+
+ // Keep track of old graph size for restoring later
+ VertexSizeFirst old_graph_size = (VertexSizeFirst)vertex_stack1.size(),
+ new_graph_size = old_graph_size + 1;
+
+ // Extend subgraph
+ put(correspondence_map_1_to_2, new_vertex1, new_vertex2);
+ put(correspondence_map_2_to_1, new_vertex2, new_vertex1);
+ vertex_stack1.push(new_vertex1);
+
+ // Only output sub-graphs larger than a single vertex
+ if (new_graph_size > 1) {
+
+ // Returning false from the callback will cancel iteration
+ if (!subgraph_callback(correspondence_map_1_to_2,
+ correspondence_map_2_to_1,
+ new_graph_size)) {
+ return (false);
+ }
+ }
+
+ // Depth-first search into the state space of possible sub-graphs
+ bool continue_iteration =
+ mcgregor_common_subgraphs_internal
+ (graph1, graph2,
+ vindex_map1, vindex_map2,
+ correspondence_map_1_to_2, correspondence_map_2_to_1,
+ vertex_stack1,
+ edges_equivalent, vertices_equivalent,
+ only_connected_subgraphs, subgraph_callback);
+
+ if (!continue_iteration) {
+ return (false);
+ }
+
+ // Restore previous state
+ if (vertex_stack1.size() > old_graph_size) {
+
+ VertexFirst stack_vertex1 = vertex_stack1.top();
+ VertexSecond stack_vertex2 = get(correspondence_map_1_to_2,
+ stack_vertex1);
+
+ // Contract subgraph
+ put(correspondence_map_1_to_2, stack_vertex1,
+ graph_traits::null_vertex());
+
+ put(correspondence_map_2_to_1, stack_vertex2,
+ graph_traits::null_vertex());
+
+ vertex_stack1.pop();
+ }
+
+ } // if can_extend_graph
+
+ } // BGL_FORALL_VERTICES_T (graph2)
+
+ } // BGL_FORALL_VERTICES_T (graph1)
+
+ return (true);
+ }
+
+ // Internal method that initializes blank correspondence maps and
+ // a vertex stack for use in mcgregor_common_subgraphs_internal.
+ template
+ inline void mcgregor_common_subgraphs_internal_init
+ (const GraphFirst& graph1,
+ const GraphSecond& graph2,
+ const VertexIndexMapFirst vindex_map1,
+ const VertexIndexMapSecond vindex_map2,
+ EdgeEquivalencePredicate edges_equivalent,
+ VertexEquivalencePredicate vertices_equivalent,
+ bool only_connected_subgraphs,
+ SubGraphInternalCallback subgraph_callback)
+ {
+ typedef mcgregor_common_subgraph_traits SubGraphTraits;
+
+ typename SubGraphTraits::correspondence_map_first_to_second_type
+ correspondence_map_1_to_2(num_vertices(graph1), vindex_map1);
+
+ BGL_FORALL_VERTICES_T(vertex1, graph1, GraphFirst) {
+ put(correspondence_map_1_to_2, vertex1,
+ graph_traits::null_vertex());
+ }
+
+ typename SubGraphTraits::correspondence_map_second_to_first_type
+ correspondence_map_2_to_1(num_vertices(graph2), vindex_map2);
+
+ BGL_FORALL_VERTICES_T(vertex2, graph2, GraphSecond) {
+ put(correspondence_map_2_to_1, vertex2,
+ graph_traits::null_vertex());
+ }
+
+ typedef typename graph_traits::vertex_descriptor
+ VertexFirst;
+
+ std::stack vertex_stack1;
+
+ mcgregor_common_subgraphs_internal
+ (graph1, graph2,
+ vindex_map1, vindex_map2,
+ correspondence_map_1_to_2, correspondence_map_2_to_1,
+ vertex_stack1,
+ edges_equivalent, vertices_equivalent,
+ only_connected_subgraphs,
+ subgraph_callback);
+ }
+
+ } // namespace detail
+
+ // ==========================================================================
+
+ // Enumerates all common subgraphs present in graph1 and graph2.
+ // Continues until the search space has been fully explored or false
+ // is returned from user_callback.
+ template
+ void mcgregor_common_subgraphs
+ (const GraphFirst& graph1,
+ const GraphSecond& graph2,
+ const VertexIndexMapFirst vindex_map1,
+ const VertexIndexMapSecond vindex_map2,
+ EdgeEquivalencePredicate edges_equivalent,
+ VertexEquivalencePredicate vertices_equivalent,
+ bool only_connected_subgraphs,
+ SubGraphCallback user_callback)
+ {
+
+ detail::mcgregor_common_subgraphs_internal_init
+ (graph1, graph2,
+ vindex_map1, vindex_map2,
+ edges_equivalent, vertices_equivalent,
+ only_connected_subgraphs,
+ user_callback);
+ }
+
+ // Variant of mcgregor_common_subgraphs with all default parameters
+ template
+ void mcgregor_common_subgraphs
+ (const GraphFirst& graph1,
+ const GraphSecond& graph2,
+ bool only_connected_subgraphs,
+ SubGraphCallback user_callback)
+ {
+
+ detail::mcgregor_common_subgraphs_internal_init
+ (graph1, graph2,
+ get(vertex_index, graph1), get(vertex_index, graph2),
+ always_equivalent(), always_equivalent(),
+ only_connected_subgraphs, user_callback);
+ }
+
+ // Named parameter variant of mcgregor_common_subgraphs
+ template
+ void mcgregor_common_subgraphs
+ (const GraphFirst& graph1,
+ const GraphSecond& graph2,
+ bool only_connected_subgraphs,
+ SubGraphCallback user_callback,
+ const bgl_named_params& params)
+ {
+
+ detail::mcgregor_common_subgraphs_internal_init
+ (graph1, graph2,
+ choose_const_pmap(get_param(params, vertex_index1),
+ graph1, vertex_index),
+ choose_const_pmap(get_param(params, vertex_index2),
+ graph2, vertex_index),
+ choose_param(get_param(params, edges_equivalent_t()),
+ always_equivalent()),
+ choose_param(get_param(params, vertices_equivalent_t()),
+ always_equivalent()),
+ only_connected_subgraphs, user_callback);
+ }
+
+ // ==========================================================================
+
+ namespace detail {
+
+ // Binary function object that intercepts subgraphs from
+ // mcgregor_common_subgraphs_internal and maintains a cache of
+ // unique subgraphs. The user callback is invoked for each unique
+ // subgraph.
+ template
+ struct unique_subgraph_interceptor {
+
+ typedef typename graph_traits::vertices_size_type
+ VertexSizeFirst;
+
+ typedef mcgregor_common_subgraph_traits SubGraphTraits;
+
+ typedef typename SubGraphTraits::correspondence_map_first_to_second_type
+ CachedCorrespondenceMapFirstToSecond;
+
+ typedef typename SubGraphTraits::correspondence_map_second_to_first_type
+ CachedCorrespondenceMapSecondToFirst;
+
+ typedef std::pair > SubGraph;
+
+ typedef std::vector SubGraphList;
+
+ unique_subgraph_interceptor(const GraphFirst& graph1,
+ const GraphSecond& graph2,
+ const VertexIndexMapFirst vindex_map1,
+ const VertexIndexMapSecond vindex_map2,
+ SubGraphCallback user_callback) :
+ m_graph1(graph1), m_graph2(graph2),
+ m_vindex_map1(vindex_map1), m_vindex_map2(vindex_map2),
+ m_subgraphs(make_shared()),
+ m_user_callback(user_callback) { }
+
+ template
+ bool operator()(CorrespondenceMapFirstToSecond correspondence_map_1_to_2,
+ CorrespondenceMapSecondToFirst correspondence_map_2_to_1,
+ VertexSizeFirst subgraph_size) {
+
+ for (typename SubGraphList::const_iterator
+ subgraph_iter = m_subgraphs->begin();
+ subgraph_iter != m_subgraphs->end();
+ ++subgraph_iter) {
+
+ SubGraph subgraph_cached = *subgraph_iter;
+
+ // Compare subgraph sizes
+ if (subgraph_size != subgraph_cached.first) {
+ continue;
+ }
+
+ if (!are_property_maps_different(correspondence_map_1_to_2,
+ subgraph_cached.second.first,
+ m_graph1)) {
+
+ // New subgraph is a duplicate
+ return (true);
+ }
+ }
+
+ // Subgraph is unique, so make a cached copy
+ CachedCorrespondenceMapFirstToSecond
+ new_subgraph_1_to_2 = CachedCorrespondenceMapFirstToSecond
+ (num_vertices(m_graph1), m_vindex_map1);
+
+ CachedCorrespondenceMapSecondToFirst
+ new_subgraph_2_to_1 = CorrespondenceMapSecondToFirst
+ (num_vertices(m_graph2), m_vindex_map2);
+
+ BGL_FORALL_VERTICES_T(vertex1, m_graph1, GraphFirst) {
+ put(new_subgraph_1_to_2, vertex1, get(correspondence_map_1_to_2, vertex1));
+ }
+
+ BGL_FORALL_VERTICES_T(vertex2, m_graph2, GraphFirst) {
+ put(new_subgraph_2_to_1, vertex2, get(correspondence_map_2_to_1, vertex2));
+ }
+
+ m_subgraphs->push_back(std::make_pair(subgraph_size,
+ std::make_pair(new_subgraph_1_to_2,
+ new_subgraph_2_to_1)));
+
+ return (m_user_callback(correspondence_map_1_to_2,
+ correspondence_map_2_to_1,
+ subgraph_size));
+ }
+
+ private:
+ const GraphFirst& m_graph1;
+ const GraphFirst& m_graph2;
+ const VertexIndexMapFirst m_vindex_map1;
+ const VertexIndexMapSecond m_vindex_map2;
+ shared_ptr m_subgraphs;
+ SubGraphCallback m_user_callback;
+ };
+
+ } // namespace detail
+
+ // Enumerates all unique common subgraphs between graph1 and graph2.
+ // The user callback is invoked for each unique subgraph as they are
+ // discovered.
+ template
+ void mcgregor_common_subgraphs_unique
+ (const GraphFirst& graph1,
+ const GraphSecond& graph2,
+ const VertexIndexMapFirst vindex_map1,
+ const VertexIndexMapSecond vindex_map2,
+ EdgeEquivalencePredicate edges_equivalent,
+ VertexEquivalencePredicate vertices_equivalent,
+ bool only_connected_subgraphs,
+ SubGraphCallback user_callback)
+ {
+ detail::unique_subgraph_interceptor unique_callback
+ (graph1, graph2,
+ vindex_map1, vindex_map2,
+ user_callback);
+
+ detail::mcgregor_common_subgraphs_internal_init
+ (graph1, graph2,
+ vindex_map1, vindex_map2,
+ edges_equivalent, vertices_equivalent,
+ only_connected_subgraphs, unique_callback);
+ }
+
+ // Variant of mcgregor_common_subgraphs_unique with all default
+ // parameters.
+ template
+ void mcgregor_common_subgraphs_unique
+ (const GraphFirst& graph1,
+ const GraphSecond& graph2,
+ bool only_connected_subgraphs,
+ SubGraphCallback user_callback)
+ {
+ mcgregor_common_subgraphs_unique
+ (graph1, graph2,
+ get(vertex_index, graph1), get(vertex_index, graph2),
+ always_equivalent(), always_equivalent(),
+ only_connected_subgraphs, user_callback);
+ }
+
+ // Named parameter variant of mcgregor_common_subgraphs_unique
+ template
+ void mcgregor_common_subgraphs_unique
+ (const GraphFirst& graph1,
+ const GraphSecond& graph2,
+ bool only_connected_subgraphs,
+ SubGraphCallback user_callback,
+ const bgl_named_params& params)
+ {
+ mcgregor_common_subgraphs_unique
+ (graph1, graph2,
+ choose_const_pmap(get_param(params, vertex_index1),
+ graph1, vertex_index),
+ choose_const_pmap(get_param(params, vertex_index2),
+ graph2, vertex_index),
+ choose_param(get_param(params, edges_equivalent_t()),
+ always_equivalent()),
+ choose_param(get_param(params, vertices_equivalent_t()),
+ always_equivalent()),
+ only_connected_subgraphs, user_callback);
+ }
+
+ // ==========================================================================
+
+ namespace detail {
+
+ // Binary function object that intercepts subgraphs from
+ // mcgregor_common_subgraphs_internal and maintains a cache of the
+ // largest subgraphs.
+ template
+ struct maximum_subgraph_interceptor {
+
+ typedef typename graph_traits::vertices_size_type
+ VertexSizeFirst;
+
+ typedef mcgregor_common_subgraph_traits SubGraphTraits;
+
+ typedef typename SubGraphTraits::correspondence_map_first_to_second_type
+ CachedCorrespondenceMapFirstToSecond;
+
+ typedef typename SubGraphTraits::correspondence_map_second_to_first_type
+ CachedCorrespondenceMapSecondToFirst;
+
+ typedef std::pair > SubGraph;
+
+ typedef std::vector SubGraphList;
+
+ maximum_subgraph_interceptor(const GraphFirst& graph1,
+ const GraphSecond& graph2,
+ const VertexIndexMapFirst vindex_map1,
+ const VertexIndexMapSecond vindex_map2,
+ SubGraphCallback user_callback) :
+ m_graph1(graph1), m_graph2(graph2),
+ m_vindex_map1(vindex_map1), m_vindex_map2(vindex_map2),
+ m_subgraphs(make_shared()),
+ m_largest_size_so_far(make_shared(0)),
+ m_user_callback(user_callback) { }
+
+ template
+ bool operator()(CorrespondenceMapFirstToSecond correspondence_map_1_to_2,
+ CorrespondenceMapSecondToFirst correspondence_map_2_to_1,
+ VertexSizeFirst subgraph_size) {
+
+ if (subgraph_size > *m_largest_size_so_far) {
+ m_subgraphs->clear();
+ *m_largest_size_so_far = subgraph_size;
+ }
+
+ if (subgraph_size == *m_largest_size_so_far) {
+
+ // Make a cached copy
+ CachedCorrespondenceMapFirstToSecond
+ new_subgraph_1_to_2 = CachedCorrespondenceMapFirstToSecond
+ (num_vertices(m_graph1), m_vindex_map1);
+
+ CachedCorrespondenceMapSecondToFirst
+ new_subgraph_2_to_1 = CachedCorrespondenceMapSecondToFirst
+ (num_vertices(m_graph2), m_vindex_map2);
+
+ BGL_FORALL_VERTICES_T(vertex1, m_graph1, GraphFirst) {
+ put(new_subgraph_1_to_2, vertex1, get(correspondence_map_1_to_2, vertex1));
+ }
+
+ BGL_FORALL_VERTICES_T(vertex2, m_graph2, GraphFirst) {
+ put(new_subgraph_2_to_1, vertex2, get(correspondence_map_2_to_1, vertex2));
+ }
+
+ m_subgraphs->push_back(std::make_pair(subgraph_size,
+ std::make_pair(new_subgraph_1_to_2,
+ new_subgraph_2_to_1)));
+ }
+
+ return (true);
+ }
+
+ void output_subgraphs() {
+ for (typename SubGraphList::const_iterator
+ subgraph_iter = m_subgraphs->begin();
+ subgraph_iter != m_subgraphs->end();
+ ++subgraph_iter) {
+
+ SubGraph subgraph_cached = *subgraph_iter;
+ m_user_callback(subgraph_cached.second.first,
+ subgraph_cached.second.second,
+ subgraph_cached.first);
+ }
+ }
+
+ private:
+ const GraphFirst& m_graph1;
+ const GraphFirst& m_graph2;
+ const VertexIndexMapFirst m_vindex_map1;
+ const VertexIndexMapSecond m_vindex_map2;
+ shared_ptr m_subgraphs;
+ shared_ptr m_largest_size_so_far;
+ SubGraphCallback m_user_callback;
+ };
+
+ } // namespace detail
+
+ // Enumerates the largest common subgraphs found between graph1
+ // and graph2. Note that the ENTIRE search space is explored before
+ // user_callback is actually invoked.
+ template
+ void mcgregor_common_subgraphs_maximum
+ (const GraphFirst& graph1,
+ const GraphSecond& graph2,
+ const VertexIndexMapFirst vindex_map1,
+ const VertexIndexMapSecond vindex_map2,
+ EdgeEquivalencePredicate edges_equivalent,
+ VertexEquivalencePredicate vertices_equivalent,
+ bool only_connected_subgraphs,
+ SubGraphCallback user_callback)
+ {
+ detail::maximum_subgraph_interceptor
+ max_interceptor
+ (graph1, graph2, vindex_map1, vindex_map2, user_callback);
+
+ detail::mcgregor_common_subgraphs_internal_init
+ (graph1, graph2,
+ vindex_map1, vindex_map2,
+ edges_equivalent, vertices_equivalent,
+ only_connected_subgraphs, max_interceptor);
+
+ // Only output the largest subgraphs
+ max_interceptor.output_subgraphs();
+ }
+
+ // Variant of mcgregor_common_subgraphs_maximum with all default
+ // parameters.
+ template
+ void mcgregor_common_subgraphs_maximum
+ (const GraphFirst& graph1,
+ const GraphSecond& graph2,
+ bool only_connected_subgraphs,
+ SubGraphCallback user_callback)
+ {
+ mcgregor_common_subgraphs_maximum
+ (graph1, graph2,
+ get(vertex_index, graph1), get(vertex_index, graph2),
+ always_equivalent(), always_equivalent(),
+ only_connected_subgraphs, user_callback);
+ }
+
+ // Named parameter variant of mcgregor_common_subgraphs_maximum
+ template
+ void mcgregor_common_subgraphs_maximum
+ (const GraphFirst& graph1,
+ const GraphSecond& graph2,
+ bool only_connected_subgraphs,
+ SubGraphCallback user_callback,
+ const bgl_named_params& params)
+ {
+ mcgregor_common_subgraphs_maximum
+ (graph1, graph2,
+ choose_const_pmap(get_param(params, vertex_index1),
+ graph1, vertex_index),
+ choose_const_pmap(get_param(params, vertex_index2),
+ graph2, vertex_index),
+ choose_param(get_param(params, edges_equivalent_t()),
+ always_equivalent()),
+ choose_param(get_param(params, vertices_equivalent_t()),
+ always_equivalent()),
+ only_connected_subgraphs, user_callback);
+ }
+
+ // ==========================================================================
+
+ namespace detail {
+
+ // Binary function object that intercepts subgraphs from
+ // mcgregor_common_subgraphs_internal and maintains a cache of the
+ // largest, unique subgraphs.
+ template
+ struct unique_maximum_subgraph_interceptor {
+
+ typedef typename graph_traits::vertices_size_type
+ VertexSizeFirst;
+
+ typedef mcgregor_common_subgraph_traits SubGraphTraits;
+
+ typedef typename SubGraphTraits::correspondence_map_first_to_second_type
+ CachedCorrespondenceMapFirstToSecond;
+
+ typedef typename SubGraphTraits::correspondence_map_second_to_first_type
+ CachedCorrespondenceMapSecondToFirst;
+
+ typedef std::pair > SubGraph;
+
+ typedef std::vector SubGraphList;
+
+ unique_maximum_subgraph_interceptor(const GraphFirst& graph1,
+ const GraphSecond& graph2,
+ const VertexIndexMapFirst vindex_map1,
+ const VertexIndexMapSecond vindex_map2,
+ SubGraphCallback user_callback) :
+ m_graph1(graph1), m_graph2(graph2),
+ m_vindex_map1(vindex_map1), m_vindex_map2(vindex_map2),
+ m_subgraphs(make_shared()),
+ m_largest_size_so_far(make_shared(0)),
+ m_user_callback(user_callback) { }
+
+ template
+ bool operator()(CorrespondenceMapFirstToSecond correspondence_map_1_to_2,
+ CorrespondenceMapSecondToFirst correspondence_map_2_to_1,
+ VertexSizeFirst subgraph_size) {
+
+ if (subgraph_size > *m_largest_size_so_far) {
+ m_subgraphs->clear();
+ *m_largest_size_so_far = subgraph_size;
+ }
+
+ if (subgraph_size == *m_largest_size_so_far) {
+
+ // Check if subgraph is unique
+ for (typename SubGraphList::const_iterator
+ subgraph_iter = m_subgraphs->begin();
+ subgraph_iter != m_subgraphs->end();
+ ++subgraph_iter) {
+
+ SubGraph subgraph_cached = *subgraph_iter;
+
+ if (!are_property_maps_different(correspondence_map_1_to_2,
+ subgraph_cached.second.first,
+ m_graph1)) {
+
+ // New subgraph is a duplicate
+ return (true);
+ }
+ }
+
+ // Subgraph is unique, so make a cached copy
+ CachedCorrespondenceMapFirstToSecond
+ new_subgraph_1_to_2 = CachedCorrespondenceMapFirstToSecond
+ (num_vertices(m_graph1), m_vindex_map1);
+
+ CachedCorrespondenceMapSecondToFirst
+ new_subgraph_2_to_1 = CachedCorrespondenceMapSecondToFirst
+ (num_vertices(m_graph2), m_vindex_map2);
+
+ BGL_FORALL_VERTICES_T(vertex1, m_graph1, GraphFirst) {
+ put(new_subgraph_1_to_2, vertex1, get(correspondence_map_1_to_2, vertex1));
+ }
+
+ BGL_FORALL_VERTICES_T(vertex2, m_graph2, GraphFirst) {
+ put(new_subgraph_2_to_1, vertex2, get(correspondence_map_2_to_1, vertex2));
+ }
+
+ m_subgraphs->push_back(std::make_pair(subgraph_size,
+ std::make_pair(new_subgraph_1_to_2,
+ new_subgraph_2_to_1)));
+ }
+
+ return (true);
+ }
+
+ void output_subgraphs() {
+ for (typename SubGraphList::const_iterator
+ subgraph_iter = m_subgraphs->begin();
+ subgraph_iter != m_subgraphs->end();
+ ++subgraph_iter) {
+
+ SubGraph subgraph_cached = *subgraph_iter;
+ m_user_callback(subgraph_cached.second.first,
+ subgraph_cached.second.second,
+ subgraph_cached.first);
+ }
+ }
+
+ private:
+ const GraphFirst& m_graph1;
+ const GraphFirst& m_graph2;
+ const VertexIndexMapFirst m_vindex_map1;
+ const VertexIndexMapSecond m_vindex_map2;
+ shared_ptr m_subgraphs;
+ shared_ptr m_largest_size_so_far;
+ SubGraphCallback m_user_callback;
+ };
+
+ } // namespace detail
+
+ // Enumerates the largest, unique common subgraphs found between
+ // graph1 and graph2. Note that the ENTIRE search space is explored
+ // before user_callback is actually invoked.
+ template
+ void mcgregor_common_subgraphs_maximum_unique
+ (const GraphFirst& graph1,
+ const GraphSecond& graph2,
+ const VertexIndexMapFirst vindex_map1,
+ const VertexIndexMapSecond vindex_map2,
+ EdgeEquivalencePredicate edges_equivalent,
+ VertexEquivalencePredicate vertices_equivalent,
+ bool only_connected_subgraphs,
+ SubGraphCallback user_callback)
+ {
+ detail::unique_maximum_subgraph_interceptor
+ unique_max_interceptor
+ (graph1, graph2, vindex_map1, vindex_map2, user_callback);
+
+ detail::mcgregor_common_subgraphs_internal_init
+ (graph1, graph2,
+ vindex_map1, vindex_map2,
+ edges_equivalent, vertices_equivalent,
+ only_connected_subgraphs, unique_max_interceptor);
+
+ // Only output the largest, unique subgraphs
+ unique_max_interceptor.output_subgraphs();
+ }
+
+ // Variant of mcgregor_common_subgraphs_maximum_unique with all default parameters
+ template
+ void mcgregor_common_subgraphs_maximum_unique
+ (const GraphFirst& graph1,
+ const GraphSecond& graph2,
+ bool only_connected_subgraphs,
+ SubGraphCallback user_callback)
+ {
+
+ mcgregor_common_subgraphs_maximum_unique
+ (graph1, graph2,
+ get(vertex_index, graph1), get(vertex_index, graph2),
+ always_equivalent(), always_equivalent(),
+ only_connected_subgraphs, user_callback);
+ }
+
+ // Named parameter variant of
+ // mcgregor_common_subgraphs_maximum_unique
+ template
+ void mcgregor_common_subgraphs_maximum_unique
+ (const GraphFirst& graph1,
+ const GraphSecond& graph2,
+ bool only_connected_subgraphs,
+ SubGraphCallback user_callback,
+ const bgl_named_params& params)
+ {
+ mcgregor_common_subgraphs_maximum_unique
+ (graph1, graph2,
+ choose_const_pmap(get_param(params, vertex_index1),
+ graph1, vertex_index),
+ choose_const_pmap(get_param(params, vertex_index2),
+ graph2, vertex_index),
+ choose_param(get_param(params, edges_equivalent_t()),
+ always_equivalent()),
+ choose_param(get_param(params, vertices_equivalent_t()),
+ always_equivalent()),
+ only_connected_subgraphs, user_callback);
+ }
+
+ // ==========================================================================
+
+ // Fills a membership map (vertex -> bool) using the information
+ // present in correspondence_map_1_to_2. Every vertex in a
+ // membership map will have a true value only if it is not
+ // associated with a null vertex in the correspondence map.
+ template
+ void fill_membership_map
+ (const GraphFirst& graph1,
+ const CorrespondenceMapFirstToSecond correspondence_map_1_to_2,
+ MembershipMapFirst membership_map1) {
+
+ BGL_FORALL_VERTICES_T(vertex1, graph1, GraphFirst) {
+ put(membership_map1, vertex1,
+ get(correspondence_map_1_to_2, vertex1) != graph_traits::null_vertex());
+ }
+
+ }
+
+ // Traits associated with a membership map filtered graph. Provided
+ // for convenience to access graph and vertex filter types.
+ template
+ struct membership_filtered_graph_traits {
+ typedef property_map_filter vertex_filter_type;
+ typedef filtered_graph graph_type;
+ };
+
+ // Returns a filtered sub-graph of graph whose edge and vertex
+ // inclusion is dictated by membership_map.
+ template
+ typename membership_filtered_graph_traits::graph_type
+ make_membership_filtered_graph
+ (const Graph& graph,
+ MembershipMap& membership_map) {
+
+ typedef membership_filtered_graph_traits MFGTraits;
+ typedef typename MFGTraits::graph_type MembershipFilteredGraph;
+
+ typename MFGTraits::vertex_filter_type v_filter(membership_map);
+
+ return (MembershipFilteredGraph(graph, keep_all(), v_filter));
+
+ }
+
+} // namespace boost
+
+#endif // BOOST_GRAPH_MCGREGOR_COMMON_SUBGRAPHS_HPP
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index c0490018..29e3e019 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -47,6 +47,7 @@ boost_test_run(biconnected_components_test)
boost_test_run(cuthill_mckee_ordering)
boost_test_run(king_ordering)
boost_test_run(matching_test)
+boost_test_run(mcgregor_subgraphs_test)
# boost_test_run(max_flow_test)
# boost_test_run(kolmogorov_max_flow_test) TODO: Boost 1.34.x only
diff --git a/test/Jamfile.v2 b/test/Jamfile.v2
index 4150773a..ee143cb2 100644
--- a/test/Jamfile.v2
+++ b/test/Jamfile.v2
@@ -118,6 +118,7 @@ test-suite graph_test :
[ run clustering_coefficient.cpp ]
[ run core_numbers_test.cpp ]
[ run read_propmap.cpp ]
+ [ run mcgregor_subgraphs_test.cpp ]
$(optional_tests)
;
diff --git a/test/mcgregor_subgraphs_test.cpp b/test/mcgregor_subgraphs_test.cpp
new file mode 100644
index 00000000..33546bf0
--- /dev/null
+++ b/test/mcgregor_subgraphs_test.cpp
@@ -0,0 +1,470 @@
+//=======================================================================
+// Copyright 2009 Trustees of Indiana University.
+// Authors: Michael Hansen
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//=======================================================================
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+using namespace boost;
+
+bool was_common_subgraph_found = false, output_graphs = false;
+std::vector simple_subgraph_list;
+
+// Callback that compares incoming graphs to the supplied common
+// subgraph.
+template
+struct test_callback {
+
+ test_callback(Graph& common_subgraph,
+ const Graph& graph1,
+ const Graph& graph2) :
+ m_graph1(graph1),
+ m_graph2(graph2),
+ m_common_subgraph(common_subgraph) { }
+
+ template
+ bool operator()(CorrespondenceMapFirstToSecond correspondence_map_1_to_2,
+ CorrespondenceMapSecondToFirst correspondence_map_2_to_1,
+ typename graph_traits::vertices_size_type subgraph_size) {
+
+ typedef typename graph_traits::vertex_descriptor Vertex;
+ typedef typename graph_traits::edge_descriptor Edge;
+ typedef std::pair EdgeInfo;
+
+ typedef typename property_map::type VertexIndexMap;
+ typedef typename property_map::type VertexNameMap;
+ typedef typename property_map::type EdgeNameMap;
+
+ if (subgraph_size != num_vertices(m_common_subgraph)) {
+ return (true);
+ }
+
+ // Fill membership maps for both graphs
+ typedef shared_array_property_map MembershipMap;
+
+ MembershipMap membership_map1(num_vertices(m_graph1),
+ get(vertex_index, m_graph1));
+
+ MembershipMap membership_map2(num_vertices(m_graph2),
+ get(vertex_index, m_graph2));
+
+ fill_membership_map(m_graph1, correspondence_map_1_to_2, membership_map1);
+ fill_membership_map(m_graph2, correspondence_map_2_to_1, membership_map2);
+
+ // Generate filtered graphs using membership maps
+ typedef typename membership_filtered_graph_traits::graph_type
+ MembershipFilteredGraph;
+
+ MembershipFilteredGraph subgraph1 =
+ make_membership_filtered_graph(m_graph1, membership_map1);
+
+ MembershipFilteredGraph subgraph2 =
+ make_membership_filtered_graph(m_graph2, membership_map2);
+
+ VertexIndexMap vindex_map1 = get(vertex_index, subgraph1);
+ VertexIndexMap vindex_map2 = get(vertex_index, subgraph2);
+
+ VertexNameMap vname_map_common = get(vertex_name, m_common_subgraph);
+ VertexNameMap vname_map1 = get(vertex_name, subgraph1);
+ VertexNameMap vname_map2 = get(vertex_name, subgraph2);
+
+ EdgeNameMap ename_map_common = get(edge_name, m_common_subgraph);
+ EdgeNameMap ename_map1 = get(edge_name, subgraph1);
+ EdgeNameMap ename_map2 = get(edge_name, subgraph2);
+
+ // Verify that subgraph1 matches the supplied common subgraph
+ BGL_FORALL_VERTICES_T(vertex1, subgraph1, MembershipFilteredGraph) {
+
+ Vertex vertex_common = vertex(get(vindex_map1, vertex1), m_common_subgraph);
+
+ // Match vertex names
+ if (get(vname_map_common, vertex_common) != get(vname_map1, vertex1)) {
+
+ // Keep looking
+ return (true);
+
+ }
+
+ BGL_FORALL_VERTICES_T(vertex1_2, subgraph1, MembershipFilteredGraph) {
+
+ Vertex vertex_common2 = vertex(get(vindex_map1, vertex1_2), m_common_subgraph);
+ EdgeInfo edge_common = edge(vertex_common, vertex_common2, m_common_subgraph);
+ EdgeInfo edge1 = edge(vertex1, vertex1_2, subgraph1);
+
+ if ((edge_common.second != edge1.second) ||
+ ((edge_common.second && edge1.second) &&
+ (get(ename_map_common, edge_common.first) != get(ename_map1, edge1.first)))) {
+
+ // Keep looking
+ return (true);
+
+ }
+ }
+
+ } // BGL_FORALL_VERTICES_T (subgraph1)
+
+ // Verify that subgraph2 matches the supplied common subgraph
+ BGL_FORALL_VERTICES_T(vertex2, subgraph2, MembershipFilteredGraph) {
+
+ Vertex vertex_common = vertex(get(vindex_map2, vertex2), m_common_subgraph);
+
+ // Match vertex names
+ if (get(vname_map_common, vertex_common) != get(vname_map2, vertex2)) {
+
+ // Keep looking
+ return (true);
+
+ }
+
+ BGL_FORALL_VERTICES_T(vertex2_2, subgraph2, MembershipFilteredGraph) {
+
+ Vertex vertex_common2 = vertex(get(vindex_map2, vertex2_2), m_common_subgraph);
+ EdgeInfo edge_common = edge(vertex_common, vertex_common2, m_common_subgraph);
+ EdgeInfo edge2 = edge(vertex2, vertex2_2, subgraph2);
+
+ if ((edge_common.second != edge2.second) ||
+ ((edge_common.second && edge2.second) &&
+ (get(ename_map_common, edge_common.first) != get(ename_map2, edge2.first)))) {
+
+ // Keep looking
+ return (true);
+
+ }
+ }
+
+ } // BGL_FORALL_VERTICES_T (subgraph2)
+
+ // Check isomorphism just to be thorough
+ if (verify_isomorphism(subgraph1, subgraph2, correspondence_map_1_to_2)) {
+
+ was_common_subgraph_found = true;
+
+ if (output_graphs) {
+
+ std::fstream file_subgraph("found_common_subgraph.dot", std::fstream::out);
+ write_graphviz(file_subgraph, subgraph1,
+ make_label_writer(get(vertex_name, m_graph1)),
+ make_label_writer(get(edge_name, m_graph1)));
+
+ }
+
+ // Stop iterating
+ return (false);
+
+ }
+
+ // Keep looking
+ return (true);
+ }
+
+private:
+ const Graph& m_graph1, m_graph2;
+ Graph& m_common_subgraph;
+};
+
+template
+struct simple_callback {
+
+ simple_callback(const Graph& graph1) :
+ m_graph1(graph1) { }
+
+ template
+ bool operator()(CorrespondenceMapFirstToSecond correspondence_map_1_to_2,
+ CorrespondenceMapSecondToFirst correspondence_map_2_to_1,
+ typename graph_traits::vertices_size_type subgraph_size) {
+
+ typedef typename graph_traits::vertex_descriptor Vertex;
+
+ typedef typename property_map::type VertexIndexMap;
+ typedef typename property_map::type VertexNameMap;
+ typedef typename property_map::type EdgeNameMap;
+
+ std::stringstream subgraph_string;
+
+ BGL_FORALL_VERTICES_T(vertex1, m_graph1, Graph) {
+
+ Vertex vertex2 = get(correspondence_map_1_to_2, vertex1);
+
+ if (vertex2 != graph_traits::null_vertex()) {
+ subgraph_string << vertex1 << "," << vertex2 << " ";
+ }
+
+ }
+
+ simple_subgraph_list.push_back(subgraph_string.str());
+
+ return (true);
+ }
+
+private:
+ const Graph& m_graph1;
+
+};
+
+template
+void add_random_vertices(Graph& graph, RandomNumberGenerator& generator,
+ int vertices_to_create, int max_edges_per_vertex,
+ VertexNameMap vname_map, EdgeNameMap ename_map) {
+
+ typedef typename graph_traits::vertex_descriptor Vertex;
+ typedef std::vector VertexList;
+
+ VertexList new_vertices;
+
+ for (int v_index = 0; v_index < vertices_to_create; ++v_index) {
+
+ Vertex new_vertex = add_vertex(graph);
+ put(vname_map, new_vertex, generator());
+ new_vertices.push_back(new_vertex);
+
+ }
+
+ // Add edges for every new vertex. Care is taken to avoid parallel
+ // edges.
+ for (typename VertexList::const_iterator v_iter = new_vertices.begin();
+ v_iter != new_vertices.end(); ++v_iter) {
+
+ Vertex source_vertex = *v_iter;
+ int edges_for_vertex = (std::min)((int)(generator() % max_edges_per_vertex) + 1,
+ (int)num_vertices(graph));
+
+ while (edges_for_vertex > 0) {
+
+ Vertex target_vertex = random_vertex(graph, generator);
+
+ if (source_vertex == target_vertex) {
+ continue;
+ }
+
+ BGL_FORALL_OUTEDGES_T(source_vertex, edge, graph, Graph) {
+ if (target(edge, graph) == target_vertex) {
+ continue;
+ }
+ }
+
+ put(ename_map, add_edge(source_vertex, target_vertex, graph).first,
+ generator());
+
+ edges_for_vertex--;
+ }
+ }
+}
+
+bool has_subgraph_string(std::string set_string) {
+ return (std::find(simple_subgraph_list.begin(), simple_subgraph_list.end(),
+ set_string) != simple_subgraph_list.end());
+}
+
+int test_main (int argc, char *argv[]) {
+ int vertices_to_create = 10;
+ int max_edges_per_vertex = 2;
+ std::size_t random_seed = time(0);
+
+ if (argc > 1) {
+ vertices_to_create = lexical_cast(argv[1]);
+ }
+
+ if (argc > 2) {
+ max_edges_per_vertex = lexical_cast(argv[2]);
+ }
+
+ if (argc > 3) {
+ output_graphs = lexical_cast(argv[3]);
+ }
+
+ if (argc > 4) {
+ random_seed = lexical_cast(argv[4]);
+ }
+
+ minstd_rand generator(random_seed);
+
+ // Using a vecS graph here so that we don't have to mess around with
+ // a vertex index map; it will be implicit.
+ typedef adjacency_list >,
+ property > Graph;
+
+ typedef graph_traits::vertex_descriptor Vertex;
+ typedef graph_traits::edge_descriptor Edge;
+
+ typedef property_map