1118 lines
42 KiB
C++
1118 lines
42 KiB
C++
//=======================================================================
|
|
// 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< 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);
|
|
|
|
// 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
|