Description
Here is my code. My code is trying to find common subgraph between graph1 and graph2 with specific number of vertices.
#include <iostream>
#include <unordered_map>
#include <string>
#include <boost/algorithm/string.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/vf2_sub_graph_iso.hpp>
#include <boost/graph/mcgregor_common_subgraphs.hpp>
#include <boost/property_map/property_map.hpp>
using EdgeProperty = boost::property<boost::edge_name_t, unsigned int>;
using VertexProperty = boost::property<boost::vertex_name_t, unsigned int, boost::property<boost::vertex_index_t, int> >;
using Graph = boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS, VertexProperty, EdgeProperty>;
template<typename GraphFirst, typename GraphSecond>
struct generate_subgraph_callback {
generate_subgraph_callback(const GraphFirst &graph1, const GraphSecond &graph2,
std::vector <Graph> *result, int size) :
m_graph1(graph1), m_graph2(graph2), m_result(result), m_subgraph_size(size) {}
template<typename CorrespondenceMapFirstToSecond, typename CorrespondenceMapSecondToFirst>
bool operator()(CorrespondenceMapFirstToSecond correspondence_map_1_to_2,
CorrespondenceMapSecondToFirst correspondence_map_2_to_1,
typename boost::graph_traits<GraphFirst>::vertices_size_type subgraph_size) {
// only when size equals input
if (subgraph_size != m_subgraph_size) {
return (true);
}
Graph subgraph;
std::vector<int> vertex_set; // vertex_set contains old graph id
std::unordered_map<int, int> map;
BGL_FORALL_VERTICES_T(vertex1, m_graph1, GraphFirst){
// skip unmapped vertices
if (boost::get(correspondence_map_1_to_2, vertex1) != boost::graph_traits<GraphSecond>::null_vertex()) {
vertex_set.push_back(vertex1);
}
}
// reconstruct vertices
int index = 0;
for (auto it = vertex_set.begin(); it != vertex_set.end(); it++) {
boost::add_vertex(VertexProperty(boost::get(boost::vertex_name_t(), m_graph1, *it)), subgraph);
std::cout << *it << " " << get(boost::vertex_name_t(), m_graph1, *it) << " " << index << std::endl;
map[*it] = index;
index++;
}
// reconstruct edges
for (auto it = vertex_set.begin(); it != vertex_set.end(); it++) {
int index1 = map[*it];
for (auto _it = std::next(it); _it != vertex_set.end(); _it++) {
int index2 = map[*_it];
// check edge exists
if (boost::edge(*it, *_it, m_graph1).second) {
boost::add_edge(index1, index2,
boost::get(boost::edge_name_t(), m_graph1, boost::edge(*it, *_it, m_graph1).first), subgraph);
} else if(boost::edge(*_it, *it, m_graph1).second) {
boost::add_edge(index2, index1,
boost::get(boost::edge_name_t(), m_graph1, boost::edge(*_it, *it, m_graph1).first), subgraph);
}
}
}
(*m_result).push_back(subgraph);
return (true);
}
private:
const GraphFirst &m_graph1;
const GraphSecond &m_graph2;
std::vector <Graph> *m_result;
int m_subgraph_size;
};
std::vector <Graph> find_maximal_common_subgraphs_n_vertices(const Graph& g1, const Graph& g2, int n) {
// use boost::mcgregor_common_subgraphs
auto vertex_comp = boost::make_property_map_equivalent(boost::get(boost::vertex_name, g1), boost::get(boost::vertex_name, g2));
auto edge_comp = boost::make_property_map_equivalent(boost::get(boost::edge_name, g1), boost::get(boost::edge_name, g2));
std::vector <Graph> result;
// store each common subgraph into result
generate_subgraph_callback<Graph, Graph> callback(g1, g2, &result, n);
boost::mcgregor_common_subgraphs_maximum_unique(g1, g2, true, callback, boost::edges_equivalent(edge_comp).vertices_equivalent(vertex_comp));
return result;
}
int main(){
Graph graph1;
boost::add_vertex(VertexProperty(11), graph1);
boost::add_vertex(VertexProperty(12), graph1);
boost::add_vertex(VertexProperty(13), graph1);
boost::add_vertex(VertexProperty(10), graph1);
boost::add_edge(0, 1, EdgeProperty(1), graph1);
boost::add_edge(1, 2, EdgeProperty(1), graph1);
boost::add_edge(2, 3, EdgeProperty(1), graph1);
boost::add_edge(3, 1, EdgeProperty(1), graph1);
Graph graph2;
boost::add_vertex(VertexProperty(11), graph2);
boost::add_vertex(VertexProperty(12), graph2);
boost::add_vertex(VertexProperty(13), graph2);
boost::add_vertex(VertexProperty(10), graph2);
boost::add_edge(0, 1, EdgeProperty(1), graph2);
boost::add_edge(1, 2, EdgeProperty(1), graph2);
boost::add_edge(2, 3, EdgeProperty(1), graph2);
boost::add_edge(3, 0, EdgeProperty(1), graph2);
std::vector <Graph> result = find_maximal_common_subgraphs_n_vertices(graph1, graph2, 4);
std::cout << result.size() << std::endl;
return 0;
}
It is clear that 11->12->13->10 should be a common subgraph with 4 vertices while result size is 0. I looked at source code of function mcgregor_common_subgraphs and I believe the reason is in below code segment.
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;
}
Here when subgraph is a graph containing nodes 11,12,13 and we want to extend node 10, mcgregor_common_subgraphs finds out there is an edge between 10 and 11 in subgraph2 while ther e is no edge between 10 and 11 in subgraph1. Hence can_extend_graph will return false. I believe the implementation of mcgregor_common_subgraphs now can only find subgraphs that appears "fully" in both graphs, which will miss many common subgraphs.