2
0
mirror of https://github.com/boostorg/graph.git synced 2026-02-09 11:12:28 +00:00

improved the isomorphism algorithm

[SVN r9845]
This commit is contained in:
Jeremy Siek
2001-04-21 20:32:08 +00:00
parent 23f017862e
commit 7dbf526742

View File

@@ -10,23 +10,44 @@
// UNDER CONSTRUCTION
#include <boost/graph/detail/set_adaptor.hpp>
#include <boost/pending/indirect_cmp.hpp>
#include <boost/graph/detail/permutation.hpp>
#include <boost/graph/named_function_params.hpp>
#include <boost/graph/filtered_graph.hpp>
#include <boost/pending/integer_range.hpp>
#include <boost/limits.hpp>
namespace boost {
//===========================================================================
// A simple but slow implementation based on description of the direct
// isomorphism algorithm in "The Graph Isomorphism Problem" by Scott
// Fortin, which was adapted from "Combinatorial Algorithms: Theory
// and Practice" by Reingold, Nievergelt, and Deo.
//A simple but slow implementation based on description of the
//direct isomorphism algorithm in "The Graph Isomorphism Problem" by
//Scott Fortin and the description in "Combinatorial Algorithms:
//Theory and Practice" by Reingold, Nievergelt, and Deo. The ideas
//behind the algorithm were independently invented by Sussenguth,
//E. H., Jr., "A Graph Theoretic Algorithm for Matching Chemical
//Structures," J. Chem. Doc., 5 (1965), 36-43 and Unger, S. H, "GIT
//- a Heuristic Program for Testing Pairs of Directed Line Graphs
//for Isomorphism," Comm. ACM, 7 (1964), 26-34.
//
// The plan is to eventually replace this with the faster algorithm
// used in "nauty" by Brendan McKay, the beginnings of which is at
// the bottom of this file.
namespace detail {
template <typename Graph>
struct degree_vertex_invariant {
typedef typename graph_traits<Graph>::degree_size_type result_type;
result_type
operator()(typename graph_traits<Graph>::vertex_descriptor v,
const Graph& g)
{
return out_degree(v, g);
}
};
namespace detail {
template <typename InvarMap, typename MultMap>
struct compare_invariant_multiplicity_predicate
{
@@ -47,169 +68,245 @@ namespace boost {
return compare_invariant_multiplicity_predicate<InvarMap, MultMap>(i,m);
}
// forall i in g1, (i,k) in g1 iff (f[i], j) in g2
template <typename Vertex, typename IndexMapping,
typename Graph1, typename Graph2>
bool can_match(Vertex k, Vertex j, IndexMapping f,
const Graph1& g1, const Graph2& g2)
template <class VertexIndexMap, class Graph>
struct isomorph_edge_ordering_predicate {
isomorph_edge_ordering_predicate(VertexIndexMap vip,
const Graph& g)
: m_index_map(vip), m_g(g) { }
template <class Edge>
bool operator()(const Edge& e1, const Edge& e2) const {
return std::max(get(m_index_map, source(e1, m_g)),
get(m_index_map, target(e1, m_g)))
< std::max(get(m_index_map, source(e2, m_g)),
get(m_index_map, target(e2, m_g)));
}
VertexIndexMap m_index_map;
const Graph& m_g;
};
template <class VertexIndexMap, class G>
inline isomorph_edge_ordering_predicate<VertexIndexMap,G>
isomorph_edge_ordering(VertexIndexMap vip, const G& g)
{
typedef typename property_traits<IndexMapping>::value_type Idx;
typename graph_traits<Graph1>::vertex_iterator i, i_end;
for (tie(i, i_end) = vertices(g1); i != i_end; ++i)
// only look at already mapped vertices
if(f[*i] != std::numeric_limits<Idx>::max())
if (edge(*i, k, g1).second) {
if (!edge(f[*i], j, g2).second)
return false;
} else
if (edge(f[*i], j, g2).second)
return false;
return true;
return isomorph_edge_ordering_predicate<VertexIndexMap,G>(vip, g);
}
template <typename Set, typename Graph1, typename Graph2,
typename VertexIter, typename IndexMapping,
typename Invar1, typename Invar2>
bool isomorph(const Set& s, const Graph1& g1, const Graph2& g2,
VertexIter k, VertexIter last,
IndexMapping f, Invar1 invar1, Invar2 invar2)
template <class VertexIter, class EdgeIter, class Graph1, class Graph2,
class V1IndexMap, class V2IndexMap, class IndexMapping,
class Invar1, class Invar2, class Set>
bool isomorph(VertexIter k, VertexIter last,
EdgeIter edge_iter, EdgeIter edge_iter_end,
const Graph1& g1, const Graph2& g2,
V1IndexMap v1_index_map,
V2IndexMap v2_index_map,
IndexMapping f, Invar1 invar1, Invar2 invar2,
const Set& not_in_S)
{
typedef typename graph_traits<Graph2>::vertex_descriptor v2_desc_t;
if (k == last)
return true;
std::vector<int> my_f(num_vertices(g1));
for (int i = 0; i < num_vertices(g1); ++i)
my_f[i] = f[i];
std::vector<v2_desc_t> my_f_vec(num_vertices(g1));
iterator_property_map<typename std::vector<v2_desc_t>::iterator,
V1IndexMap> my_f(my_f_vec.begin(), v1_index_map);
typedef typename vertex_subset_compliment_filter
<Graph2, Set>::type Subgraph;
Subgraph s_g2 =
make_vertex_subset_compliment_filter(const_cast<Graph2&>(g2), s);
typename graph_traits<Subgraph>::vertex_iterator j, j_end;
for (tie(j, j_end) = vertices(s_g2); j != j_end; ++j) {
if ( (invar1[*k] != invar2[*j]) || ! can_match(*k, *j, f, g1, g2) )
continue;
my_f[*k] = *j;
Set s_j(s);
set_insert(s_j, *j);
if (isomorph(s_j, g1, g2, boost::next(k), last, my_f.begin(),
invar1, invar2)) {
for (int c = 0; c < num_vertices(g1); ++c)
f[c] = my_f[c];
typename graph_traits<Graph1>::vertex_iterator i1, i1_end;
for (tie(i1, i1_end) = vertices(g1); i1 != i1_end; ++i1)
my_f[*i1] = f[*i1];
// Collect the potential vertices
int k_id = get(v1_index_map, *k);
std::vector<v2_desc_t> vertex_set;
std::copy(not_in_S.begin(), not_in_S.end(),
std::back_inserter(vertex_set));
// Weed out vertices that do not have the same connectivity as k
for (; edge_iter != edge_iter_end
&& (k_id == std::max(get(v1_index_map, source(*edge_iter, g1)),
get(v1_index_map, target(*edge_iter, g1))));
++edge_iter) {
std::vector<v2_desc_t> tmp, adj;
if (k_id == get(v1_index_map, source(*edge_iter, g1))) {
v2_desc_t v = f[target(*edge_iter, g1)];
typename graph_traits<Graph2>::out_edge_iterator ei, ei_end;
for (tie(ei, ei_end) = out_edges(v, g2); ei != ei_end; ++ei)
if (invar1[*k] == invar2[target(*ei, g2)])
adj.push_back(target(*ei, g2));
std::set_intersection(adj.begin(), adj.end(),
vertex_set.begin(), vertex_set.end(),
std::back_inserter(tmp),
not_in_S.key_comp());
} else {
v2_desc_t v = f[source(*edge_iter, g1)];
typename graph_traits<Graph2>::in_edge_iterator ei, ei_end;
for (tie(ei, ei_end) = in_edges(v, g2); ei != ei_end; ++ei)
if (invar1[*k] == invar2[source(*ei, g2)])
adj.push_back(source(*ei, g2));
std::set_intersection(adj.begin(), adj.end(),
vertex_set.begin(), vertex_set.end(),
std::back_inserter(tmp),
not_in_S.key_comp());
}
std::swap(vertex_set, tmp);
if (vertex_set.empty())
break;
}
for (int j = 0; j < vertex_set.size(); ++j) {
my_f[*k] = vertex_set[j];
Set my_not_in_S(not_in_S);
set_remove(my_not_in_S, vertex_set[j]);
if (isomorph(boost::next(k), last, edge_iter, edge_iter_end, g1, g2,
v1_index_map, v2_index_map,
my_f, invar1, invar2, my_not_in_S)) {
for (tie(i1, i1_end) = vertices(g1); i1 != i1_end; ++i1)
f[*i1] = my_f[*i1];
return true;
}
}
return false;
}
template <typename Graph1, typename Graph2,
typename IndexMapping, typename VertexInvariant,
typename V1Map, typename V2Map>
bool isomorphism_impl(const Graph1& g1,
const Graph2& g2,
IndexMapping f,
VertexInvariant invariant,
V1Map v1_index_map,
V2Map v2_index_map)
{
typedef typename graph_traits<Graph1>::vertices_size_type size_type;
typename graph_traits<Graph1>::vertex_iterator i1, i1_end;
typename graph_traits<Graph2>::vertex_iterator i2, i2_end;
// Quick return with false if |V1| != |V2|
if (num_vertices(g1) != num_vertices(g2))
return false;
typedef typename VertexInvariant::result_type InvarValue;
typedef std::vector<InvarValue> invar_vec;
invar_vec invar1_vec(num_vertices(g1)), invar2_vec(num_vertices(g2));
iterator_property_map<typename invar_vec::iterator, V1Map>
invar1(invar1_vec.begin(), v1_index_map);
iterator_property_map<typename invar_vec::iterator, V2Map>
invar2(invar2_vec.begin(), v2_index_map);
for (tie(i1, i1_end) = vertices(g1); i1 != i1_end; ++i1)
invar1[*i1] = invariant(*i1, g1);
for (tie(i2, i2_end) = vertices(g2); i2 != i2_end; ++i2)
invar2[*i2] = invariant(*i2, g2);
{ // check if the graph's invariants do not match
invar_vec invar1_tmp(invar1_vec), invar2_tmp(invar2_vec);
std::sort(invar1_tmp.begin(), invar1_tmp.end());
std::sort(invar2_tmp.begin(), invar2_tmp.end());
if (! std::equal(invar1_tmp.begin(), invar1_tmp.end(),
invar2_tmp.begin()))
return false;
}
// compute invariant multiplicity
std::vector<std::size_t> invar_mult(num_vertices(g1), 0);
for (tie(i1, i1_end) = vertices(g1); i1 != i1_end; ++i1)
++invar_mult[invar1[*i1]];
// calculate the permutation that would sort vertices based on
// invariant multiplicity
std::vector<size_type> perm;
integer_range<size_type> range(0, num_vertices(g1));
std::copy(range.begin(), range.end(), std::back_inserter(perm));
std::sort(perm.begin(), perm.end(),
detail::compare_invariant_multiplicity(invar1_vec.begin(),
invar_mult.begin()));
typedef typename graph_traits<Graph1>::vertex_descriptor VertexG1;
std::vector<VertexG1> g1_vertices;
for (tie(i1, i1_end) = vertices(g1); i1 != i1_end; ++i1)
g1_vertices.push_back(*i1);
permute(g1_vertices.begin(), g1_vertices.end(), perm.begin());
std::vector<typename graph_traits<Graph1>::edge_descriptor> edge_set;
std::copy(edges(g1).first, edges(g1).second,
std::back_inserter(edge_set));
std::sort(edge_set.begin(), edge_set.end(),
detail::isomorph_edge_ordering
(make_iterator_property_map(perm.begin(), v1_index_map), g1));
std::vector<VertexG1>::iterator first = g1_vertices.begin();
typename graph_traits<Graph2>::vertex_iterator vi, vi_end;
for (tie(vi, vi_end) = vertices(g2); vi != vi_end; ++vi) {
f[*first] = *vi;
typedef typename graph_traits<Graph1>::vertex_descriptor VertexG2;
typedef typename property_traits<V2Map>::value_type V2Idx;
typedef indirect_cmp<V2Map, std::less<V2Idx> > Cmp;
Cmp cmp(v2_index_map);
std::set<VertexG2, Cmp> not_in_S(cmp);
for (tie(i2, i2_end) = vertices(g2); i2 != i2_end; ++i2)
set_insert(not_in_S, *i2);
set_remove(not_in_S, *vi);
if(detail::isomorph
(boost::next(first), g1_vertices.end(),
edge_set.begin(), edge_set.end(),
g1, g2,
make_iterator_property_map(perm.begin(), v1_index_map),
v2_index_map, f, invar1, invar2, not_in_S))
return true;
}
return false;
}
} // namespace detail
template <typename Graph>
struct degree_vertex_invariant {
typedef typename graph_traits<Graph>::degree_size_type result_type;
result_type
operator()(typename graph_traits<Graph>::vertex_descriptor v,
const Graph& g)
{
return out_degree(v, g);
}
};
template <typename Graph1, typename Graph2,
typename IndexMapping, typename VertexInvariant>
template <typename Graph1, typename Graph2, class P, class T, class R>
bool isomorphism(const Graph1& g1,
const Graph2& g2,
IndexMapping f,
VertexInvariant invariant)
const bgl_named_params<P,T,R>& params)
{
typedef typename graph_traits<Graph1>::vertices_size_type v_size_t;
typename graph_traits<Graph1>::vertex_iterator i1, i1_end;
typename graph_traits<Graph2>::vertex_iterator i2, i2_end;
typedef typename VertexInvariant::result_type InvarValue;
typedef std::vector<InvarValue> invar_vec;
if (num_vertices(g1) != num_vertices(g2))
return false;
// Initialize f to infinity
typedef typename property_traits<IndexMapping>::value_type Idx;
for (tie(i1, i1_end) = vertices(g1); i1 != i1_end; ++i1)
f[*i1] = std::numeric_limits<Idx>::max();
invar_vec invar1(num_vertices(g1)), invar2(num_vertices(g2));
for (tie(i1, i1_end) = vertices(g1); i1 != i1_end; ++i1)
invar1[*i1] = invariant(*i1, g1);
for (tie(i2, i2_end) = vertices(g2); i2 != i2_end; ++i2)
invar2[*i2] = invariant(*i2, g2);
{ // check if the graph's invariants do not match
invar_vec invar1_tmp(invar1), invar2_tmp(invar2);
std::sort(invar1_tmp.begin(), invar1_tmp.end());
std::sort(invar2_tmp.begin(), invar2_tmp.end());
if (! std::equal(invar1_tmp.begin(), invar1_tmp.end(),
invar2_tmp.begin()))
return false;
}
//-------------------------------------------------------------------------
// reorder vertices of g1 based on invariant multiplicity
typedef typename graph_traits<Graph1>::vertex_descriptor VertexG1;
std::vector<VertexG1> g1_vertices;
for (tie(i1, i1_end) = vertices(g1); i1 != i1_end; ++i1)
g1_vertices.push_back(*i1);
{
// set up permutation
std::vector<int> perm(num_vertices(g1));
for (int i = 0; i != num_vertices(g1); ++i)
perm[i] = i;
// compute invariant multiplicity
std::vector<std::size_t> invar_mult(num_vertices(g1), 0);
for (tie(i1, i1_end) = vertices(g1); i1 != i1_end; ++i1)
++invar_mult[invar1[*i1]];
// calculate the permutation that would sort vertices based on
// invariant multiplicity
std::sort(perm.begin(), perm.end(),
detail::compare_invariant_multiplicity(invar1.begin(),
invar_mult.begin()));
// permute g1's vertices
inplace_permute(g1_vertices.begin(), g1_vertices.end(), perm.begin());
// permute invar1 to match
inplace_permute(invar1.begin(), invar1.end(), perm.begin());
}
std::set<int> s;
return detail::isomorph(s, g1, g2,
g1_vertices.begin(), g1_vertices.end(),
f, invar1.begin(), invar2.begin());
typedef typename graph_traits<Graph2>::vertex_descriptor v2_desc_t;
std::vector<v2_desc_t>::size_type
n = is_default_param(get_param(params, vertex_isomorphism_t()))
? num_vertices(g1) : 0;
std::vector<v2_desc_t> f(n);
degree_vertex_invariant<Graph1> default_invar;
return detail::isomorphism_impl
(g1, g2,
choose_param
(get_param(params, vertex_isomorphism_t()),
make_iterator_property_map
(f.begin(),
choose_const_pmap(get_param(params, vertex_index1),
g1, vertex_index))),
choose_param(get_param(params, vertex_invariant_t()),
default_invar),
choose_const_pmap(get_param(params, vertex_index1),
g1, vertex_index),
choose_const_pmap(get_param(params, vertex_index2),
g2, vertex_index)
);
}
template <typename Graph1, typename Graph2, typename IndexMapping>
bool isomorphism(const Graph1& g1,
const Graph2& g2,
IndexMapping f)
{
return isomorphism(g1, g2, f, degree_vertex_invariant<Graph1>());
}
template <typename Graph1, typename Graph2>
bool is_isomorphic(const Graph1& g1, const Graph2& g2)
bool isomorphism(const Graph1& g1, const Graph2& g2)
{
typedef typename graph_traits<Graph1>::vertices_size_type size_type;
std::vector<int> f(num_vertices(g1));
return isomorphism(g1, g2, f.begin());
typedef typename graph_traits<Graph2>::vertex_descriptor v2_desc_t;
std::vector<v2_desc_t> f(num_vertices(g1));
degree_vertex_invariant<Graph1> invariant;
return detail::isomorphism_impl
(g1, g2,
make_iterator_property_map(f.begin(),
get(vertex_index, g1)),
invariant,
get(vertex_index, g1),
get(vertex_index, g2)
);
}
#if 0
// The beginnings of a C++ implementation of canonical labelling
@@ -220,7 +317,7 @@ namespace boost {
//=========================================================================
// Mathematical Set Concept
//
// bool set_equal(a,b)
// bool set_equal(a, b)
// bool is_subset(a, b)
// bool is_proper_subset(a, b)
// void set_intersect(a, b, c)