Commit 3e5e39e5 authored by Tiago Peixoto's avatar Tiago Peixoto
Browse files

Include vertex randomization in subgraph_isomorphism()

This allows for sampling of random subgraph isomorphisms.
parent a5d75e84
......@@ -61,22 +61,35 @@ struct get_subgraphs
{
template <class Graph1, class Graph2, class VertexLabel,
class EdgeLabel>
void operator()(const Graph1& g1, const Graph2* g2,
void operator()(const Graph1& sub, const Graph2* g,
VertexLabel vertex_label1, boost::any vertex_label2,
EdgeLabel edge_label1, boost::any edge_label2,
vector<vector<pair<size_t,size_t> > >& F, size_t max_n)
const
vector<vector<pair<size_t, size_t> > >& F,
vector<size_t>& vlist, pair<size_t,size_t> sn) const
{
typedef PropLabelling<Graph1,Graph2,VertexLabel,VertexLabel>
vlabelling_t;
typedef PropLabelling<Graph1,Graph2,EdgeLabel,EdgeLabel>
elabelling_t;
size_t seed = sn.first;
size_t max_n = sn.second;
rng_t rng(static_cast<rng_t::result_type>(seed));
vlist.resize(num_vertices(*g));
int i, N = num_vertices(*g);
for (i = 0; i < N; ++i)
vlist[i] = i;
for (i = 0; i < N - 1; ++i)
{
tr1::uniform_int<> random(i, N - 1);
swap(vlist[i], vlist[random(rng)]);
}
subgraph_isomorphism
(g1, *g2, vlabelling_t(g1, *g2, vertex_label1,
(sub, *g, vlabelling_t(sub, *g, vertex_label1,
any_cast<VertexLabel>(vertex_label2)),
elabelling_t(g1, *g2, edge_label1,
any_cast<EdgeLabel>(edge_label2)), F, max_n);
elabelling_t(sub, *g, edge_label1,
any_cast<EdgeLabel>(edge_label2)), F, vlist, max_n);
}
};
......@@ -84,32 +97,34 @@ struct get_mapping
{
template <class Graph1, class Graph2, class EdgeLabel, class VertexMap,
class EdgeMap, class EdgeIndexMap>
void operator()(const Graph1& g1, const Graph2* g2, EdgeLabel edge_label1,
void operator()(const Graph1& sub, const Graph2* g, EdgeLabel edge_label1,
boost::any edge_label2, vector<pair<size_t, size_t> >& F,
VertexMap vmapping, EdgeMap emapping,
EdgeIndexMap edge_index2) const
EdgeIndexMap edge_index2, vector<size_t>& vlist) const
{
typedef PropLabelling<Graph1,Graph2,EdgeLabel,EdgeLabel>
elabelling_t;
elabelling_t edge_labelling(g1, *g2, edge_label1,
elabelling_t edge_labelling(sub, *g, edge_label1,
any_cast<EdgeLabel>(edge_label2));
int i, N = F.size();
#pragma omp parallel for default(shared) private(i) schedule(dynamic)
for (i = 0; i < N; ++i)
{
if (vertex(i, g1) == graph_traits<Graph1>::null_vertex())
if (vertex(i, sub) == graph_traits<Graph1>::null_vertex())
continue;
vmapping[vertex(F[i].first, g1)] = vertex(F[i].second, *g2);
vmapping[vertex(F[i].first, sub)] = vertex(vlist[F[i].second], *g);
typename graph_traits<Graph1>::out_edge_iterator e, e_end;
for (tie(e, e_end) = out_edges(vertex(i, g1), g1); e != e_end; ++e)
for (tie(e, e_end) = out_edges(vertex(i, sub), sub); e != e_end;
++e)
{
bool found = false;
typename graph_traits<Graph2>::out_edge_iterator e2, e2_end;
for (tie(e2, e2_end) = out_edges(vertex(F[i].second, *g2), *g2);
for (tie(e2, e2_end) =
out_edges(vertex(vlist[F[i].second], *g), *g);
e2 != e2_end; ++e2)
{
if (target(*e2, *g2) ==
vertex(F[target(*e, g1)].second, *g2) &&
if (target(*e2, *g) ==
vertex(vlist[F[target(*e, sub)].second], *g) &&
edge_labelling(*e, *e2))
{
emapping[*e] = edge_index2[*e2];
......@@ -149,7 +164,7 @@ void subgraph_isomorphism(GraphInterface& gi1, GraphInterface& gi2,
boost::any vertex_label1, boost::any vertex_label2,
boost::any edge_label1, boost::any edge_label2,
python::list vmapping, python::list emapping,
size_t max_n)
size_t max_n, size_t seed)
{
if (gi1.GetDirected() != gi2.GetDirected())
return;
......@@ -167,13 +182,14 @@ void subgraph_isomorphism(GraphInterface& gi1, GraphInterface& gi2,
}
vector<vector<pair<size_t,size_t> > > F;
vector<size_t> vlist;
if (gi1.GetDirected())
{
run_action<graph_tool::detail::always_directed>()
(gi1, bind<void>(get_subgraphs(),
_1, _2, _3, vertex_label2, _4, edge_label2,
ref(F), max_n),
ref(F), ref(vlist), make_pair(seed, max_n)),
directed_graph_view_pointers(), vertex_props_t(),
edge_props_t())
(gi2.GetGraphView(), vertex_label1, edge_label1);
......@@ -183,7 +199,7 @@ void subgraph_isomorphism(GraphInterface& gi1, GraphInterface& gi2,
run_action<graph_tool::detail::never_directed>()
(gi1, bind<void>(get_subgraphs(),
_1, _2, _3, vertex_label2, _4, edge_label2,
ref(F), max_n),
ref(F), ref(vlist), make_pair(seed, max_n)),
undirected_graph_view_pointers(), vertex_props_t(),
edge_props_t())
(gi2.GetGraphView(), vertex_label1, edge_label1);
......@@ -205,7 +221,7 @@ void subgraph_isomorphism(GraphInterface& gi1, GraphInterface& gi2,
(gi1, bind<void>(get_mapping(),
_1, _2, _3, edge_label2,
ref(F[i]), ref(vm), ref(ep),
gi2.GetEdgeIndex()),
gi2.GetEdgeIndex(), ref(vlist)),
directed_graph_view_pointers(), edge_props_t())
(gi2.GetGraphView(), edge_label1);
}
......@@ -215,7 +231,7 @@ void subgraph_isomorphism(GraphInterface& gi1, GraphInterface& gi2,
(gi1, bind<void>(get_mapping(),
_1, _2, _3, edge_label2,
ref(F[i]), ref(vm), ref(ep),
gi2.GetEdgeIndex()),
gi2.GetEdgeIndex(), ref(vlist)),
undirected_graph_view_pointers(), edge_props_t())
(gi2.GetGraphView(), edge_label1);
}
......
......@@ -21,18 +21,19 @@
#include <boost/graph/graph_traits.hpp>
#include <utility>
#include <tr1/unordered_set>
#include <tr1/random>
namespace boost
{
using namespace std;
typedef tr1::mt19937 rng_t;
namespace detail {
//sparse matrix
typedef vector<tr1::unordered_set<size_t> > matrix_t;
struct check_adjacency
{
template <class Graph>
......@@ -79,26 +80,28 @@ struct check_adjacency
bool operator()(typename graph_traits<Graph1>::vertex_descriptor k,
typename graph_traits<Graph2>::vertex_descriptor l,
matrix_t& M, EdgeLabelling& edge_labelling,
Graph1& g1, Graph2& g2, mpl::true_ directed)
Graph1& g1, Graph2& g2, vector<size_t>& vindex,
mpl::true_ directed)
{
return do_check(k, l, M, edge_labelling, g1, g2, mpl::true_()) &&
do_check(k, l, M, edge_labelling, g1, g2, mpl::false_());
return do_check(k, l, M, edge_labelling, g1, g2, vindex,
mpl::true_()) &&
do_check(k, l, M, edge_labelling, g1, g2, vindex, mpl::false_());
}
template <class Graph1, class Graph2, class EdgeLabelling>
bool operator()(typename graph_traits<Graph1>::vertex_descriptor k,
typename graph_traits<Graph2>::vertex_descriptor l,
matrix_t& M, EdgeLabelling& edge_labelling,
Graph1& g1, Graph2& g2, mpl::false_ directed)
matrix_t& M, EdgeLabelling& edge_labelling, Graph1& g1,
Graph2& g2, vector<size_t>& vindex, mpl::false_ directed)
{
return do_check(k, l, M, edge_labelling, g1, g2, mpl::true_());
return do_check(k, l, M, edge_labelling, g1, g2, vindex, mpl::true_());
}
template <class Graph1, class Graph2, class EdgeLabelling, class IsOut>
bool do_check(typename graph_traits<Graph1>::vertex_descriptor k,
typename graph_traits<Graph2>::vertex_descriptor l,
matrix_t& M, EdgeLabelling& edge_labelling, Graph1& g1,
Graph2& g2, IsOut)
Graph2& g2, vector<size_t>& vindex, IsOut)
{
bool valid = true;
typename get_edge_iterator<Graph1, IsOut>::type e1, e1_end;
......@@ -118,7 +121,8 @@ struct check_adjacency
typename graph_traits<Graph2>::vertex_descriptor v2 =
get_vertex(*e2, g2, IsOut());
if (M[v1].find(v2) != M[v1].end() && edge_labelling(*e1, *e2))
if (M[v1].find(vindex[v2]) != M[v1].end() &&
edge_labelling(*e1, *e2))
{
is_adjacent = true;
break;
......@@ -137,13 +141,14 @@ struct check_adjacency
template <class Graph1, class Graph2, class EdgeLabelling>
bool refine_check(const Graph1& g1, const Graph2& g2, matrix_t& M, size_t count,
bool refine_check(const Graph1& sub, const Graph2& g, matrix_t& M, size_t count,
tr1::unordered_set<size_t>& already_mapped,
EdgeLabelling edge_labelling)
EdgeLabelling edge_labelling, vector<size_t>& vlist,
vector<size_t>& vindex)
{
matrix_t M_temp(num_vertices(g1));
matrix_t M_temp(num_vertices(sub));
int k = 0, N = num_vertices(g1);
int k = 0, N = num_vertices(sub);
#pragma omp parallel for default(shared) private(k) schedule(dynamic)
for (k = 0; k < int(count); ++k)
M_temp[k] = M[k];
......@@ -159,7 +164,7 @@ bool refine_check(const Graph1& g1, const Graph2& g2, matrix_t& M, size_t count,
{
if (abort)
continue;
if (vertex(k, g1) == graph_traits<Graph1>::null_vertex())
if (vertex(k, sub) == graph_traits<Graph1>::null_vertex())
continue;
tr1::unordered_set<size_t> m_new;
for (typeof(M[k].begin()) li = M[k].begin(); li != M[k].end(); ++li)
......@@ -168,7 +173,8 @@ bool refine_check(const Graph1& g1, const Graph2& g2, matrix_t& M, size_t count,
if (already_mapped.find(l) != already_mapped.end())
continue;
bool valid = check_adjacency()
(vertex(k, g1), vertex(l, g2), M, edge_labelling, g1, g2,
(vertex(k, sub), vertex(vlist[l], g), M, edge_labelling,
sub, g, vindex,
typename is_directed::apply<Graph1>::type());
if (valid)
m_new.insert(l);
......@@ -191,26 +197,26 @@ bool refine_check(const Graph1& g1, const Graph2& g2, matrix_t& M, size_t count,
template <class Graph1, class Graph2, class EdgeLabelling, class Mapping>
void find_mappings(const Graph1& g1, const Graph2& g2, matrix_t M0,
void find_mappings(const Graph1& sub, const Graph2& g, matrix_t& M0,
vector<Mapping>& FF, EdgeLabelling edge_labelling,
size_t max_n)
vector<size_t>& vlist, vector<size_t>& vindex, size_t max_n)
{
size_t i = 0;
for (i=0; i < num_vertices(g1); ++i)
if (vertex(i, g1) != graph_traits<Graph1>::null_vertex())
for (i = 0; i < num_vertices(sub); ++i)
if (vertex(i, sub) != graph_traits<Graph1>::null_vertex())
break;
int last_i = 0;
for (last_i = num_vertices(g1)-1; last_i >= 0; --last_i)
if (vertex(i, g1) != graph_traits<Graph1>::null_vertex())
for (last_i = num_vertices(sub) - 1; last_i >= 0; --last_i)
if (vertex(i, sub) != graph_traits<Graph1>::null_vertex())
break;
for (; i < num_vertices(g2); ++i)
if (vertex(i, g2) != graph_traits<Graph2>::null_vertex())
for (; i < vlist.size(); ++i)
if (vertex(vlist[i], g) != graph_traits<Graph2>::null_vertex())
break;
Mapping F;
list<tuple<matrix_t, size_t,
typename matrix_t::value_type::const_iterator> > Mstack;
Mstack.push_back(make_tuple(M0,i,M0[i].begin()));
Mstack.push_back(make_tuple(M0, i, M0[i].begin()));
get<2>(Mstack.back()) = get<0>(Mstack.back())[i].begin();
tr1::unordered_set<size_t> already_mapped;
......@@ -244,12 +250,13 @@ void find_mappings(const Graph1& g1, const Graph2& g2, matrix_t M0,
++mi;
size_t ni = i + 1;
for (; ni < num_vertices(g1); ++ni)
if (vertex(ni, g1) != graph_traits<Graph1>::null_vertex())
for (; ni < num_vertices(sub); ++ni)
if (vertex(ni, sub) != graph_traits<Graph1>::null_vertex())
break;
// refine search tree
if (refine_check(g1, g2, M_prime, ni, already_mapped, edge_labelling))
if (refine_check(sub, g, M_prime, ni, already_mapped, edge_labelling,
vlist, vindex))
{
// store the current mapping so far
F.push_back(std::make_pair(i, c_mi));
......@@ -289,26 +296,30 @@ void find_mappings(const Graph1& g1, const Graph2& g2, matrix_t M0,
template <class Graph1, class Graph2, class VertexLabelling,
class EdgeLabelling, class Mapping>
void subgraph_isomorphism(const Graph1& g1, const Graph2& g2,
void subgraph_isomorphism(const Graph1& sub, const Graph2& g,
VertexLabelling vertex_labelling,
EdgeLabelling edge_labelling, vector<Mapping>& F,
size_t max_n)
vector<size_t>& vlist, size_t max_n)
{
detail::matrix_t M0(num_vertices(g1));
// initial mapping candidates
detail::matrix_t M0(num_vertices(sub));
vector<size_t> vindex(num_vertices(g));
for (size_t j = 0; j < num_vertices(g); ++j)
vindex[vlist[j]] = j;
bool abort = false;
int i, N = num_vertices(g1);
int i, N = num_vertices(sub);
#pragma omp parallel for default(shared) private(i) schedule(dynamic)
for (i = 0; i < N; ++i)
{
if (vertex(i, g1) == graph_traits<Graph1>::null_vertex() || abort)
if (vertex(i, sub) == graph_traits<Graph1>::null_vertex() || abort)
continue;
for (size_t j = 0; j < num_vertices(g2); ++j)
for (size_t j = 0; j < num_vertices(g); ++j)
{
if (vertex(j, g2) == graph_traits<Graph1>::null_vertex())
if (vertex(vlist[j], g) == graph_traits<Graph1>::null_vertex())
continue;
if (vertex_labelling(vertex(i,g1), vertex(j,g2)))
if (vertex_labelling(vertex(i, sub), vertex(vlist[j], g)))
M0[i].insert(j);
}
if (M0[i].empty())
......@@ -316,7 +327,7 @@ void subgraph_isomorphism(const Graph1& g1, const Graph2& g2,
}
if (abort)
return;
detail::find_mappings(g1, g2, M0, F, edge_labelling, max_n);
detail::find_mappings(sub, g, M0, F, edge_labelling, vlist, vindex, max_n);
}
} // namespace boost
......
......@@ -36,7 +36,7 @@ void subgraph_isomorphism(GraphInterface& gi1, GraphInterface& gi2,
boost::any vertex_label1, boost::any vertex_label2,
boost::any edge_label1, boost::any edge_label2,
python::list vmapping, python::list emapping,
size_t n_max);
size_t n_max, size_t seed);
void export_components();
void export_dists();
......
......@@ -88,11 +88,14 @@ def isomorphism(g1, g2, isomap=False):
return iso
def subgraph_isomorphism(sub, g, max_n=0):
def subgraph_isomorphism(sub, g, max_n=0, random=True):
r"""
Obtain all subgraph isomorphisms of `sub` in `g` (or at most `max_n`
subgraphs, if `max_n > 0`).
If `random` = True, the vertices of `g` are indexed in random order before
the search.
It returns two lists, containing the vertex and edge property maps for `sub`
with the isomorphism mappings. The value of the properties are the
vertex/edge index of the corresponding vertex/edge in `g`.
......@@ -150,13 +153,17 @@ def subgraph_isomorphism(sub, g, max_n=0):
elabels=(None, None)
vmaps = []
emaps = []
if random:
seed = numpy.random.randint(0, sys.maxint)
else:
seed = 42
libgraph_tool_topology.\
subgraph_isomorphism(sub._Graph__graph, g._Graph__graph,
_prop("v", sub, vlabels[0]),
_prop("v", g, vlabels[1]),
_prop("e", sub, elabels[0]),
_prop("e", g, elabels[1]),
vmaps, emaps, max_n)
vmaps, emaps, max_n, seed)
for i in xrange(len(vmaps)):
vmaps[i] = PropertyMap(vmaps[i], sub, "v")
emaps[i] = PropertyMap(emaps[i], sub, "e")
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment