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 + + + + C++ Boost +
+

+ mcgregor_common_subgraphs +

+
+// named parameter version
+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);
+
+// non-named parameter version
+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): +

    +
  1. + Verify that the vertex1 and vertex2 are + equivalent using the vertices_equivalent predicate. +
  2. +
  3. + 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. +
  4. +
  5. + 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. +
  6. +
+

+ +

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) {
+
+    // Print out correspondences between vertices
+    BGL_FORALL_VERTICES_T(vertex1, m_graph1, GraphFirst) {
+
+      // Skip unmapped vertices
+      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;
+
+};
+
+// Assuming the graph types GraphFirst and GraphSecond have already been defined
+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: +

+
+// Print out all connected common subgraphs between graph1 and graph2.
+// All vertices and edges are assumed to be equivalent and both graph1 and graph2
+// have implicit vertex index properties.
+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: +

+ +
+// Assume both graphs were defined with implicit vertex name,
+// edge name, and vertex index properties
+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);
+
+// Print out all connected common subgraphs between graph1 and 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: +

+
+// Assuming we're inside the operator() of the callback with a member variable m_graph1 representing the first graph passed to mcgregor_common_subgraphs.
+// ...
+
+// Step 1: Transform a correspondence map into a membership map. Any vertex -> bool property map will work
+typedef shared_array_property_map MembershipMap;      
+MembershipMap membership_map1(num_vertices(m_graph1), get(vertex_index, m_graph1));
+
+// Fill the membership map for m_graph1. GraphSecond is the type of the second graph given to mcgregor_common_subgraphs.
+fill_membership_map<GraphSecond>(m_graph1, correspondence_map_1_to_2, membership_map1);
+
+// Step 2: Use the membership map from Step 1 to obtain a filtered graph
+typedef typename membership_filtered_graph_traits<GraphFirst, MembershipMap>::graph_type
+  MembershipFilteredGraph;
+
+MembershipFilteredGraph subgraph1 = make_membership_filtered_graph(m_graph1, membership_map1);
+
+// The filtered graph can be used like a regular BGL graph...
+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::type VertexNameMap; + typedef property_map::type EdgeNameMap; + + // Generate a random common subgraph and then add random vertices + // and edges to the two parent graphs. + Graph common_subgraph, graph1, graph2; + + VertexNameMap vname_map_common = get(vertex_name, common_subgraph); + VertexNameMap vname_map1 = get(vertex_name, graph1); + VertexNameMap vname_map2 = get(vertex_name, graph2); + + EdgeNameMap ename_map_common = get(edge_name, common_subgraph); + EdgeNameMap ename_map1 = get(edge_name, graph1); + EdgeNameMap ename_map2 = get(edge_name, graph2); + + for (int vindex = 0; vindex < vertices_to_create; ++vindex) { + put(vname_map_common, add_vertex(common_subgraph), generator()); + } + + BGL_FORALL_VERTICES(source_vertex, common_subgraph, Graph) { + + BGL_FORALL_VERTICES(target_vertex, common_subgraph, Graph) { + + if (source_vertex != target_vertex) { + put(ename_map_common, + add_edge(source_vertex, target_vertex, common_subgraph).first, + generator()); + } + } + } + + randomize_property(common_subgraph, generator); + randomize_property(common_subgraph, generator); + + copy_graph(common_subgraph, graph1); + copy_graph(common_subgraph, graph2); + + // Randomly add vertices and edges to graph1 and graph2. + add_random_vertices(graph1, generator, vertices_to_create, + max_edges_per_vertex, vname_map1, ename_map1); + + add_random_vertices(graph2, generator, vertices_to_create, + max_edges_per_vertex, vname_map2, ename_map2); + + if (output_graphs) { + + std::fstream file_graph1("graph1.dot", std::fstream::out), + file_graph2("graph2.dot", std::fstream::out), + file_common_subgraph("expected_common_subgraph.dot", std::fstream::out); + + write_graphviz(file_graph1, graph1, + make_label_writer(vname_map1), + make_label_writer(ename_map1)); + + write_graphviz(file_graph2, graph2, + make_label_writer(vname_map2), + make_label_writer(ename_map2)); + + write_graphviz(file_common_subgraph, common_subgraph, + make_label_writer(get(vertex_name, common_subgraph)), + make_label_writer(get(edge_name, common_subgraph))); + + } + + std::cout << "Searching for common subgraph of size " << + num_vertices(common_subgraph) << std::endl; + + test_callback user_callback(common_subgraph, graph1, graph2); + + mcgregor_common_subgraphs(graph1, graph2, true, user_callback, + edges_equivalent(make_property_map_equivalent(ename_map1, ename_map2)). + vertices_equivalent(make_property_map_equivalent(vname_map1, vname_map2))); + + BOOST_CHECK(was_common_subgraph_found); + + // Test maximum and unique variants on known graphs + Graph graph_simple1, graph_simple2; + simple_callback user_callback_simple(graph_simple1); + + VertexNameMap vname_map_simple1 = get(vertex_name, graph_simple1); + VertexNameMap vname_map_simple2 = get(vertex_name, graph_simple2); + + 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); + + 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); + + // Unique subgraphs + std::cout << "Searching for unique subgraphs" << std::endl; + mcgregor_common_subgraphs_unique(graph_simple1, graph_simple2, + true, user_callback_simple, + vertices_equivalent(make_property_map_equivalent(vname_map_simple1, vname_map_simple2))); + + BOOST_CHECK(has_subgraph_string("0,0 1,1 ")); + BOOST_CHECK(has_subgraph_string("0,0 1,1 2,2 ")); + BOOST_CHECK(has_subgraph_string("0,0 2,2 ")); + BOOST_CHECK(has_subgraph_string("1,1 2,2 ")); + + if (output_graphs) { + std::copy(simple_subgraph_list.begin(), simple_subgraph_list.end(), + std::ostream_iterator(std::cout, "\n")); + + std::cout << std::endl; + } + + simple_subgraph_list.clear(); + + // Maximum subgraphs + std::cout << "Searching for maximum subgraphs" << std::endl; + mcgregor_common_subgraphs_maximum(graph_simple1, graph_simple2, + true, user_callback_simple, + vertices_equivalent(make_property_map_equivalent(vname_map_simple1, vname_map_simple2))); + + BOOST_CHECK(has_subgraph_string("0,0 1,1 2,2 ")); + + if (output_graphs) { + std::copy(simple_subgraph_list.begin(), simple_subgraph_list.end(), + std::ostream_iterator(std::cout, "\n")); + + std::cout << std::endl; + } + + simple_subgraph_list.clear(); + + // Maximum, unique subgraphs + std::cout << "Searching for maximum unique subgraphs" << std::endl; + mcgregor_common_subgraphs_maximum_unique(graph_simple1, graph_simple2, + true, user_callback_simple, + vertices_equivalent(make_property_map_equivalent(vname_map_simple1, vname_map_simple2))); + + BOOST_CHECK(simple_subgraph_list.size() == 1); + BOOST_CHECK(has_subgraph_string("0,0 1,1 2,2 ")); + + if (output_graphs) { + std::copy(simple_subgraph_list.begin(), simple_subgraph_list.end(), + std::ostream_iterator(std::cout, "\n")); + + std::cout << std::endl; + } + + return 0; +}