Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

Click here to view the latest version of this page.

boost/graph/mcgregor_common_subgraphs.hpp

//=======================================================================
// 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 <algorithm>
#include <vector>
#include <stack>

#include <boost/make_shared.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/filtered_graph.hpp>
#include <boost/graph/graph_utility.hpp>
#include <boost/graph/iteration_macros.hpp>
#include <boost/graph/properties.hpp>
#include <boost/property_map/shared_array_property_map.hpp>

namespace boost {

  namespace detail {

    // Traits associated with common subgraphs, used mainly to keep a
    // consistent type for the correspondence maps.
    template <typename GraphFirst,
              typename GraphSecond,
              typename VertexIndexMapFirst,
              typename VertexIndexMapSecond>
    struct mcgregor_common_subgraph_traits {
      typedef typename graph_traits<GraphFirst>::vertex_descriptor vertex_first_type;
      typedef typename graph_traits<GraphSecond>::vertex_descriptor vertex_second_type;
      
      typedef shared_array_property_map<vertex_second_type, VertexIndexMapFirst>
        correspondence_map_first_to_second_type;
  
      typedef shared_array_property_map<vertex_first_type, VertexIndexMapSecond>
        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 <typename PropertyMapFirst,
            typename PropertyMapSecond>
  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 <typename ItemFirst,
              typename ItemSecond>
    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 <typename PropertyMapFirst,
            typename PropertyMapSecond>
  property_map_equivalent<PropertyMapFirst,
                          PropertyMapSecond>
  make_property_map_equivalent
  (const PropertyMapFirst property_map1,
   const PropertyMapSecond property_map2) {

    return (property_map_equivalent<PropertyMapFirst, PropertyMapSecond>
            (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 <typename ItemFirst,
              typename ItemSecond>
    bool operator()(const ItemFirst&, const ItemSecond&) {
      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 <typename GraphFirst,
              typename GraphSecond,
              typename CorrespondenceMapFirstToSecond,
              typename CorrespondenceMapSecondToFirst,
              typename EdgeEquivalencePredicate,
              typename VertexEquivalencePredicate>
    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<GraphFirst>::vertices_size_type subgraph_size,
     typename graph_traits<GraphFirst>::vertex_descriptor new_vertex1,
     typename graph_traits<GraphSecond>::vertex_descriptor new_vertex2,
     EdgeEquivalencePredicate edges_equivalent,
     VertexEquivalencePredicate vertices_equivalent,
     bool only_connected_subgraphs)
    {
      typedef typename graph_traits<GraphFirst>::vertex_descriptor VertexFirst;
      typedef typename graph_traits<GraphSecond>::vertex_descriptor VertexSecond;
      
      typedef typename graph_traits<GraphFirst>::edge_descriptor EdgeFirst;
      typedef typename graph_traits<GraphSecond>::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<GraphSecond>::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 <typename GraphFirst,
              typename GraphSecond,
              typename VertexIndexMapFirst,
              typename VertexIndexMapSecond,
              typename CorrespondenceMapFirstToSecond,
              typename CorrespondenceMapSecondToFirst,
              typename VertexStackFirst,
              typename EdgeEquivalencePredicate,
              typename VertexEquivalencePredicate,
              typename SubGraphInternalCallback>
    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<GraphFirst>::vertex_descriptor VertexFirst;
      typedef typename graph_traits<GraphSecond>::vertex_descriptor VertexSecond;
      typedef typename graph_traits<GraphFirst>::vertices_size_type VertexSizeFirst;

      // Get iterators for vertices from both graphs
      typename graph_traits<GraphFirst>::vertex_iterator
        vertex1_iter, vertex1_end;
  
      typename graph_traits<GraphSecond>::vertex_iterator
        vertex2_begin, vertex2_end, vertex2_iter;
  
      boost::tie(vertex1_iter, vertex1_end) = vertices(graph1);
      boost::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<GraphSecond>::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<GraphFirst>::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<GraphSecond>::null_vertex());
            
              put(correspondence_map_2_to_1, stack_vertex2,
                  graph_traits<GraphFirst>::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 <typename GraphFirst,
              typename GraphSecond,
              typename VertexIndexMapFirst,
              typename VertexIndexMapSecond,
              typename EdgeEquivalencePredicate,
              typename VertexEquivalencePredicate,
              typename SubGraphInternalCallback>
    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<GraphFirst,
        GraphSecond, VertexIndexMapFirst,
        VertexIndexMapSecond> 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<GraphSecond>::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<GraphFirst>::null_vertex());
      }

      typedef typename graph_traits<GraphFirst>::vertex_descriptor
        VertexFirst;
        
      std::stack<VertexFirst> 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 <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)
  {
      
    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 <typename GraphFirst,
            typename GraphSecond,
            typename SubGraphCallback>
  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 <typename GraphFirst,
            typename GraphSecond,
            typename SubGraphCallback,
            typename Param,
            typename Tag,
            typename Rest>
  void mcgregor_common_subgraphs
  (const GraphFirst& graph1,
   const GraphSecond& graph2,
   bool only_connected_subgraphs,
   SubGraphCallback user_callback,
   const bgl_named_params<Param, Tag, Rest>& 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 <typename GraphFirst,
              typename GraphSecond,
              typename VertexIndexMapFirst,
              typename VertexIndexMapSecond,
              typename SubGraphCallback>
    struct unique_subgraph_interceptor {

      typedef typename graph_traits<GraphFirst>::vertices_size_type
        VertexSizeFirst;

      typedef mcgregor_common_subgraph_traits<GraphFirst, GraphSecond,
        VertexIndexMapFirst, VertexIndexMapSecond> SubGraphTraits;
        
      typedef typename SubGraphTraits::correspondence_map_first_to_second_type
        CachedCorrespondenceMapFirstToSecond;

      typedef typename SubGraphTraits::correspondence_map_second_to_first_type
        CachedCorrespondenceMapSecondToFirst;

      typedef std::pair<VertexSizeFirst,
        std::pair<CachedCorrespondenceMapFirstToSecond,
                  CachedCorrespondenceMapSecondToFirst> > SubGraph;
                
      typedef std::vector<SubGraph> 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<SubGraphList>()),
        m_user_callback(user_callback) { }
      
      template <typename CorrespondenceMapFirstToSecond,
                typename CorrespondenceMapSecondToFirst>
      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<SubGraphList> 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 <typename GraphFirst,
            typename GraphSecond,
            typename VertexIndexMapFirst,
            typename VertexIndexMapSecond,
            typename EdgeEquivalencePredicate,
            typename VertexEquivalencePredicate,
            typename SubGraphCallback>
  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<GraphFirst, GraphSecond,
      VertexIndexMapFirst, VertexIndexMapSecond,
      SubGraphCallback> 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 <typename GraphFirst,
            typename GraphSecond,
            typename SubGraphCallback>
  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 <typename GraphFirst,
            typename GraphSecond,
            typename SubGraphCallback,
            typename Param,
            typename Tag,
            typename Rest>
  void mcgregor_common_subgraphs_unique
  (const GraphFirst& graph1,
   const GraphSecond& graph2,
   bool only_connected_subgraphs,
   SubGraphCallback user_callback,
   const bgl_named_params<Param, Tag, Rest>& 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 <typename GraphFirst,
              typename GraphSecond,
              typename VertexIndexMapFirst,
              typename VertexIndexMapSecond,
              typename SubGraphCallback>
    struct maximum_subgraph_interceptor {

      typedef typename graph_traits<GraphFirst>::vertices_size_type
        VertexSizeFirst;

      typedef mcgregor_common_subgraph_traits<GraphFirst, GraphSecond,
        VertexIndexMapFirst, VertexIndexMapSecond> SubGraphTraits;
        
      typedef typename SubGraphTraits::correspondence_map_first_to_second_type
        CachedCorrespondenceMapFirstToSecond;

      typedef typename SubGraphTraits::correspondence_map_second_to_first_type
        CachedCorrespondenceMapSecondToFirst;

      typedef std::pair<VertexSizeFirst,
        std::pair<CachedCorrespondenceMapFirstToSecond,
                  CachedCorrespondenceMapSecondToFirst> > SubGraph;

      typedef std::vector<SubGraph> 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<SubGraphList>()),
        m_largest_size_so_far(make_shared<VertexSizeFirst>(0)),
        m_user_callback(user_callback) { }

      template <typename CorrespondenceMapFirstToSecond,
                typename CorrespondenceMapSecondToFirst>
      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<SubGraphList> m_subgraphs;
      shared_ptr<VertexSizeFirst> 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 <typename GraphFirst,
            typename GraphSecond,
            typename VertexIndexMapFirst,
            typename VertexIndexMapSecond,
            typename EdgeEquivalencePredicate,
            typename VertexEquivalencePredicate,
            typename SubGraphCallback>
  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<GraphFirst, GraphSecond,
      VertexIndexMapFirst, VertexIndexMapSecond, SubGraphCallback>
      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 <typename GraphFirst,
            typename GraphSecond,
            typename SubGraphCallback>
  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 <typename GraphFirst,
            typename GraphSecond,
            typename SubGraphCallback,
            typename Param,
            typename Tag,
            typename Rest>
  void mcgregor_common_subgraphs_maximum
  (const GraphFirst& graph1,
   const GraphSecond& graph2,
   bool only_connected_subgraphs,
   SubGraphCallback user_callback,
   const bgl_named_params<Param, Tag, Rest>& 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 <typename GraphFirst,
              typename GraphSecond,
              typename VertexIndexMapFirst,
              typename VertexIndexMapSecond,
              typename SubGraphCallback>
    struct unique_maximum_subgraph_interceptor {

      typedef typename graph_traits<GraphFirst>::vertices_size_type
        VertexSizeFirst;

      typedef mcgregor_common_subgraph_traits<GraphFirst, GraphSecond,
        VertexIndexMapFirst, VertexIndexMapSecond> SubGraphTraits;
        
      typedef typename SubGraphTraits::correspondence_map_first_to_second_type
        CachedCorrespondenceMapFirstToSecond;

      typedef typename SubGraphTraits::correspondence_map_second_to_first_type
        CachedCorrespondenceMapSecondToFirst;

      typedef std::pair<VertexSizeFirst,
        std::pair<CachedCorrespondenceMapFirstToSecond,
                  CachedCorrespondenceMapSecondToFirst> > SubGraph;

      typedef std::vector<SubGraph> 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<SubGraphList>()),
        m_largest_size_so_far(make_shared<VertexSizeFirst>(0)),
        m_user_callback(user_callback) { }        
      
      template <typename CorrespondenceMapFirstToSecond,
                typename CorrespondenceMapSecondToFirst>
      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<SubGraphList> m_subgraphs;
      shared_ptr<VertexSizeFirst> 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 <typename GraphFirst,
            typename GraphSecond,
            typename VertexIndexMapFirst,
            typename VertexIndexMapSecond,
            typename EdgeEquivalencePredicate,
            typename VertexEquivalencePredicate,
            typename SubGraphCallback>
  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<GraphFirst, GraphSecond,
      VertexIndexMapFirst, VertexIndexMapSecond, SubGraphCallback>
      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 <typename GraphFirst,
            typename GraphSecond,
            typename SubGraphCallback>
  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 <typename GraphFirst,
            typename GraphSecond,
            typename SubGraphCallback,
            typename Param,
            typename Tag,
            typename Rest>
  void mcgregor_common_subgraphs_maximum_unique
  (const GraphFirst& graph1,
   const GraphSecond& graph2,
   bool only_connected_subgraphs,
   SubGraphCallback user_callback,
   const bgl_named_params<Param, Tag, Rest>& 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 <typename GraphSecond,
            typename GraphFirst,
            typename CorrespondenceMapFirstToSecond,
            typename MembershipMapFirst>
  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<GraphSecond>::null_vertex());
    }

  }

  // Traits associated with a membership map filtered graph.  Provided
  // for convenience to access graph and vertex filter types.
  template <typename Graph,
            typename MembershipMap>
  struct membership_filtered_graph_traits {
    typedef property_map_filter<MembershipMap> vertex_filter_type;
    typedef filtered_graph<Graph, keep_all, vertex_filter_type> graph_type;
  };

  // Returns a filtered sub-graph of graph whose edge and vertex
  // inclusion is dictated by membership_map.
  template <typename Graph,
            typename MembershipMap>            
  typename membership_filtered_graph_traits<Graph, MembershipMap>::graph_type
  make_membership_filtered_graph
  (const Graph& graph,
   MembershipMap& membership_map) {

    typedef membership_filtered_graph_traits<Graph, MembershipMap> 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