diff --git a/src/graph/clustering/graph_clustering.cc b/src/graph/clustering/graph_clustering.cc index 3c9487117b42d0fd524fb0c313ec11f553a9147f..50f7a3419d6f6cde5ae98d2d209dc20128380215 100644 --- a/src/graph/clustering/graph_clustering.cc +++ b/src/graph/clustering/graph_clustering.cc @@ -56,8 +56,8 @@ using namespace boost::python; void extended_clustering(GraphInterface& g, python::list props); void get_motifs(GraphInterface& g, size_t k, python::list subgraph_list, - python::list hist, python::list p, bool comp_iso, - bool fill_list, rng_t& rng); + python::list hist, python::list pvmaps, bool collect_vmaps, + python::list p, bool comp_iso, bool fill_list, rng_t& rng); BOOST_PYTHON_MODULE(libgraph_tool_clustering) { diff --git a/src/graph/clustering/graph_motifs.cc b/src/graph/clustering/graph_motifs.cc index 2d0f65289ab1aefeb33e0d305d1c7e575ed160da..5aec5df79e397b9efa078d9ddec6cdaf8979248d 100644 --- a/src/graph/clustering/graph_motifs.cc +++ b/src/graph/clustering/graph_motifs.cc @@ -22,6 +22,8 @@ #include "graph_properties.hh" #include "graph_util.hh" +#include "graph_python_interface.hh" + #include "graph_motifs.hh" #include @@ -66,8 +68,8 @@ struct retrieve_from_list }; void get_motifs(GraphInterface& g, size_t k, python::list subgraph_list, - python::list hist, python::list p, bool comp_iso, - bool fill_list, rng_t& rng) + python::list hist, python::list pvmaps, bool collect_vmaps, + python::list p, bool comp_iso, bool fill_list, rng_t& rng) { boost::any list; if (g.GetDirected()) @@ -105,15 +107,29 @@ void get_motifs(GraphInterface& g, size_t k, python::list subgraph_list, else sampler = sample_some(plist, rng); + typedef property_map_type + ::apply::type + vmap_t; + vector > vmaps; + run_action<>() - (g, boost::bind(get_all_motifs(), _1, k, boost::ref(list), - boost::ref(phist), _2, - plist[0], comp_iso, fill_list, boost::ref(rng)), + (g, boost::bind(get_all_motifs(collect_vmaps, plist[0], comp_iso, + fill_list, rng), + _1, k, boost::ref(list), boost::ref(phist), + boost::ref(vmaps),_2), mpl::vector())(sampler); for (size_t i = 0; i < phist.size(); ++i) hist.append(phist[i]); + for (size_t i = 0; i < vmaps.size(); ++i) + { + python::list vlist; + for (size_t j = 0; j < vmaps[i].size(); ++j) + vlist.append(PythonPropertyMap(vmaps[i][j])); + pvmaps.append(vlist); + } + if (fill_list) { for (int i = 0; i < python::len(subgraph_list); ++i) diff --git a/src/graph/clustering/graph_motifs.hh b/src/graph/clustering/graph_motifs.hh index 7e87f811840631342c4b9763509ee92577e55b63..83f29f5e56a3736105dbbcc58852f0e166941158 100644 --- a/src/graph/clustering/graph_motifs.hh +++ b/src/graph/clustering/graph_motifs.hh @@ -288,10 +288,20 @@ void get_sig(Graph& g, vector& sig) // gets (or samples) all the subgraphs in graph g struct get_all_motifs { - template + get_all_motifs(bool collect_vmaps, double p, bool comp_iso, bool fill_list, + rng_t& rng) + : collect_vmaps(collect_vmaps), p(p), + comp_iso(comp_iso), fill_list(fill_list), rng(rng) {} + bool collect_vmaps; + double p; + bool comp_iso; + bool fill_list; + rng_t& rng; + + template void operator()(Graph& g, size_t k, boost::any& list, - vector& hist, Sampler sampler, double p, - bool comp_iso, bool fill_list, rng_t& rng) const + vector& hist, vector >& vmaps, + Sampler sampler) const { typedef typename mpl::if_::type, d_graph_t, @@ -377,6 +387,7 @@ struct get_all_motifs } bool found = false; + size_t pos; typeof(sub_list.begin()) sl = sub_list.find(sig); if (sl != sub_list.end()) { @@ -397,7 +408,8 @@ struct get_all_motifs } if (found) { - hist[sl->second[l].first]++; + pos = sl->second[l].first; + hist[pos]++; break; } } @@ -409,6 +421,17 @@ struct get_all_motifs sub_list[sig].push_back(make_pair(subgraph_list.size() - 1, sub)); hist.push_back(1); + pos = hist.size() - 1; + found = true; + } + + if (found && collect_vmaps) + { + if (pos >= vmaps.size()) + vmaps.resize(pos + 1); + vmaps[pos].push_back(VMap(get(boost::vertex_index,sub))); + for (size_t vi = 0; vi < num_vertices(sub); ++vi) + vmaps[pos].back()[vertex(vi, sub)] = subgraphs[j][vi]; } } } diff --git a/src/graph_tool/clustering/__init__.py b/src/graph_tool/clustering/__init__.py index 2363919201375545d3f8c6dc0264625303f8e480..273e341201c0a78dbcfd10ff720ae87efc16ae00 100644 --- a/src/graph_tool/clustering/__init__.py +++ b/src/graph_tool/clustering/__init__.py @@ -46,7 +46,7 @@ from __future__ import division, absolute_import, print_function from .. dl_import import dl_import dl_import("from . import libgraph_tool_clustering as _gt") -from .. import _degree, _prop, Graph, GraphView, _get_rng +from .. import _degree, _prop, Graph, GraphView, PropertyMap, _get_rng from .. topology import isomorphism from .. generation import random_rewire from .. stats import vertex_hist @@ -284,7 +284,7 @@ def extended_clustering(g, props=None, max_depth=3, undirected=False): return props -def motifs(g, k, p=1.0, motif_list=None): +def motifs(g, k, p=1.0, motif_list=None, return_maps=False): r""" Count the occurrence of k-size subgraphs (motifs). A tuple with two lists is returned: the list of motifs found, and the list with their respective @@ -296,7 +296,7 @@ def motifs(g, k, p=1.0, motif_list=None): Graph to be used. k : int number of vertices of the motifs - p : float or float list (optional, default: 1.0) + p : float or float list (optional, default: `1.0`) uniform fraction of the motifs to be sampled. If a float list is provided, it will be used as the fraction at each depth :math:`[1,\dots,k]` in the algorithm. See [wernicke-efficient-2006]_ for @@ -304,6 +304,10 @@ def motifs(g, k, p=1.0, motif_list=None): motif_list : list of :class:`~graph_tool.Graph` objects, optional If supplied, the algorithms will only search for the motifs in this list (or isomorphisms). + return_maps : bool (optional, default `False`) + If true, a list will be returned, which provide for each motif graph a + list of vertex property maps which map the motif to its location in the + main graph. Returns ------- @@ -314,6 +318,9 @@ def motifs(g, k, p=1.0, motif_list=None): out-degree-sequence, and number of edges (in this order). counts : list of ints The number of times the respective motif in the motifs list was counted + vertex_maps : list of lists of :class:`~graph_tool.PropertyMap` objects + List for each motif graph containing the locations in the main + graph. This is only returned if `return_maps == True`. See Also -------- @@ -375,9 +382,10 @@ def motifs(g, k, p=1.0, motif_list=None): pd = [float(x) for x in p] hist = [] + vertex_maps = [] was_directed = g.is_directed() - _gt.get_motifs(g._Graph__graph, k, sub_list, hist, pd, - True, len(sub_list) == 0, + _gt.get_motifs(g._Graph__graph, k, sub_list, hist, vertex_maps, + return_maps, pd, True, len(sub_list) == 0, _get_rng()) # assemble graphs @@ -402,6 +410,11 @@ def motifs(g, k, p=1.0, motif_list=None): sub_list = [x[0] for x in list_hist] hist = [x[1] for x in list_hist] + if return_maps: + for i, vlist in enumerate(vertex_maps): + sub = sub_list[i] + vertex_maps[i] = [PropertyMap(vm, sub, "v") for vm in vlist] + return sub_list, hist, vertex_maps return sub_list, hist