Skip to content

Commit 02e74a2

Browse files
committed
add: expose voronoi to python - wip
1 parent e6479b1 commit 02e74a2

4 files changed

Lines changed: 416 additions & 227 deletions

File tree

src/_igraph/graphobject.c

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13895,6 +13895,130 @@ PyObject *igraphmodule_Graph_random_walk(igraphmodule_GraphObject * self,
1389513895
}
1389613896
}
1389713897

13898+
13899+
/**********************************************************************
13900+
* Other methods *
13901+
**********************************************************************/
13902+
13903+
/**
13904+
* Voronoi clustering
13905+
*/
13906+
PyObject *igraphmodule_Graph_community_voronoi(igraphmodule_GraphObject *self,
13907+
PyObject *args, PyObject *kwds) {
13908+
static char *kwlist[] = {"lengths", "weights", "mode", "radius", NULL};
13909+
PyObject *lengths_o = Py_None, *weights_o = Py_None;
13910+
PyObject *mode_o = Py_None;
13911+
PyObject *radius_o = Py_None;
13912+
igraph_vector_t lengths_v, weights_v;
13913+
igraph_vector_int_t membership_v, generators_v;
13914+
igraph_neimode_t mode = IGRAPH_ALL;
13915+
igraph_real_t radius = -1.0; /* negative means auto-optimize */
13916+
igraph_real_t modularity;
13917+
PyObject *membership_o, *generators_o, *result_o;
13918+
igraph_bool_t return_modularity = 1;
13919+
igraph_bool_t lengths_allocated = 0, weights_allocated = 0;
13920+
13921+
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOO", kwlist,
13922+
&lengths_o, &weights_o, &mode_o, &radius_o))
13923+
return NULL;
13924+
13925+
/* Handle mode parameter */
13926+
if (mode_o != Py_None) {
13927+
if (igraphmodule_PyObject_to_neimode_t(mode_o, &mode))
13928+
return NULL;
13929+
}
13930+
13931+
/* Handle radius parameter */
13932+
if (radius_o != Py_None) {
13933+
if (PyFloat_Check(radius_o)) {
13934+
radius = PyFloat_AsDouble(radius_o);
13935+
} else if (PyLong_Check(radius_o)) {
13936+
radius = PyLong_AsDouble(radius_o);
13937+
} else {
13938+
PyErr_SetString(PyExc_TypeError, "radius must be a number or None");
13939+
return NULL;
13940+
}
13941+
if (PyErr_Occurred()) return NULL;
13942+
}
13943+
13944+
/* Handle lengths parameter */
13945+
if (lengths_o != Py_None) {
13946+
if (igraphmodule_PyObject_to_vector_t(lengths_o, &lengths_v, 1)) {
13947+
return NULL;
13948+
}
13949+
lengths_allocated = 1;
13950+
}
13951+
13952+
/* Handle weights parameter */
13953+
if (weights_o != Py_None) {
13954+
if (igraphmodule_PyObject_to_vector_t(weights_o, &weights_v, 1)) {
13955+
if (lengths_allocated) {
13956+
igraph_vector_destroy(&lengths_v);
13957+
}
13958+
return NULL;
13959+
}
13960+
weights_allocated = 1;
13961+
}
13962+
13963+
/* Initialize result vectors */
13964+
if (igraph_vector_int_init(&membership_v, 0)) {
13965+
if (lengths_allocated) igraph_vector_destroy(&lengths_v);
13966+
if (weights_allocated) igraph_vector_destroy(&weights_v);
13967+
igraphmodule_handle_igraph_error();
13968+
return NULL;
13969+
}
13970+
13971+
if (igraph_vector_int_init(&generators_v, 0)) {
13972+
if (lengths_allocated) igraph_vector_destroy(&lengths_v);
13973+
if (weights_allocated) igraph_vector_destroy(&weights_v);
13974+
igraph_vector_int_destroy(&membership_v);
13975+
igraphmodule_handle_igraph_error();
13976+
return NULL;
13977+
}
13978+
13979+
/* Call the C function - pass NULL for None parameters */
13980+
if (igraph_community_voronoi(&self->g, &membership_v, &generators_v,
13981+
return_modularity ? &modularity : NULL,
13982+
lengths_allocated ? &lengths_v : NULL,
13983+
weights_allocated ? &weights_v : NULL,
13984+
mode, radius)) {
13985+
if (lengths_allocated) igraph_vector_destroy(&lengths_v);
13986+
if (weights_allocated) igraph_vector_destroy(&weights_v);
13987+
igraph_vector_int_destroy(&membership_v);
13988+
igraph_vector_int_destroy(&generators_v);
13989+
igraphmodule_handle_igraph_error();
13990+
return NULL;
13991+
}
13992+
13993+
/* Clean up input vectors */
13994+
if (lengths_allocated) igraph_vector_destroy(&lengths_v);
13995+
if (weights_allocated) igraph_vector_destroy(&weights_v);
13996+
13997+
/* Convert results to Python objects */
13998+
membership_o = igraphmodule_vector_int_t_to_PyList(&membership_v);
13999+
igraph_vector_int_destroy(&membership_v);
14000+
if (!membership_o) {
14001+
igraph_vector_int_destroy(&generators_v);
14002+
return NULL;
14003+
}
14004+
14005+
generators_o = igraphmodule_vector_int_t_to_PyList(&generators_v);
14006+
igraph_vector_int_destroy(&generators_v);
14007+
if (!generators_o) {
14008+
Py_DECREF(membership_o);
14009+
return NULL;
14010+
}
14011+
14012+
/* Return tuple with membership, generators, and modularity */
14013+
if (return_modularity) {
14014+
result_o = Py_BuildValue("(NNd)", membership_o, generators_o, (double)modularity);
14015+
} else {
14016+
result_o = Py_BuildValue("(NN)", membership_o, generators_o);
14017+
}
14018+
14019+
return result_o;
14020+
}
14021+
1389814022
/**********************************************************************
1389914023
* Special internal methods that you won't need to mess around with *
1390014024
**********************************************************************/
@@ -18628,6 +18752,8 @@ struct PyMethodDef igraphmodule_Graph_methods[] = {
1862818752
" current membership vector any more.\n"
1862918753
"@return: the community membership vector.\n"
1863018754
},
18755+
{"community_voronoi", (PyCFunction) igraphmodule_Graph_community_voronoi,
18756+
METH_VARARGS | METH_KEYWORDS, "Finds communities using Voronoi partitioning"},
1863118757
{"community_walktrap",
1863218758
(PyCFunction) igraphmodule_Graph_community_walktrap,
1863318759
METH_VARARGS | METH_KEYWORDS,
@@ -18693,6 +18819,46 @@ struct PyMethodDef igraphmodule_Graph_methods[] = {
1869318819
"@return: a random walk that starts from the given vertex and has at most\n"
1869418820
" the given length (shorter if the random walk got stuck).\n"
1869518821
},
18822+
18823+
/****************/
18824+
/* OTHER METHODS */
18825+
/****************/{
18826+
"community_voronoi",
18827+
(PyCFunction) igraphmodule_Graph_community_voronoi,
18828+
METH_VARARGS | METH_KEYWORDS,
18829+
"community_voronoi(lengths=None, weights=None, mode=\"all\", radius=None)\n\n"
18830+
"Finds communities using Voronoi partitioning.\n\n"
18831+
"This function finds communities using a Voronoi partitioning of vertices based\n"
18832+
"on the given edge lengths divided by the edge clustering coefficient.\n"
18833+
"The generator vertices are chosen to be those with the largest local relative\n"
18834+
"density within a radius, with the local relative density of a vertex defined as\n"
18835+
"s * m / (m + k), where s is the strength of the vertex, m is the number of\n"
18836+
"edges within the vertex's first order neighborhood, while k is the number of\n"
18837+
"edges with only one endpoint within this neighborhood.\n\n"
18838+
"@param lengths: edge lengths, or C{None} to consider all edges as having\n"
18839+
" unit length. Voronoi partitioning will use edge lengths equal to\n"
18840+
" lengths / ECC where ECC is the edge clustering coefficient.\n"
18841+
"@param weights: edge weights, or C{None} to consider all edges as having\n"
18842+
" unit weight. Weights are used when selecting generator points, as well\n"
18843+
" as for computing modularity.\n"
18844+
"@param mode: if C{\"out\"}, distances from generator points to all other\n"
18845+
" nodes are considered. If C{\"in\"}, the reverse distances are used.\n"
18846+
" If C{\"all\"}, edge directions are ignored. This parameter is ignored\n"
18847+
" for undirected graphs.\n"
18848+
"@param radius: the radius/resolution to use when selecting generator points.\n"
18849+
" The larger this value, the fewer partitions there will be. Pass C{None}\n"
18850+
" to automatically select the radius that maximizes modularity.\n"
18851+
"@return: a tuple containing the membership vector, generator vertices,\n"
18852+
" and modularity score.\n"
18853+
"@rtype: tuple\n\n"
18854+
"@newfield ref: Reference\n"
18855+
"@ref: Deritei et al., Community detection by graph Voronoi diagrams,\n"
18856+
" New Journal of Physics 16, 063007 (2014)\n"
18857+
" U{https://doi.org/10.1088/1367-2630/16/6/063007}\n"
18858+
"@ref: Molnár et al., Community Detection in Directed Weighted Networks\n"
18859+
" using Voronoi Partitioning, Scientific Reports 14, 8124 (2024)\n"
18860+
" U{https://doi.org/10.1038/s41598-024-58624-4}\n"
18861+
},
1869618862

1869718863
/**********************/
1869818864
/* INTERNAL FUNCTIONS */

0 commit comments

Comments
 (0)