Skip to content

Commit e624df9

Browse files
committed
feat: added weights=... argument to Graph.radius() and Graph.eccentricity()
1 parent b7d33f2 commit e624df9

File tree

3 files changed

+74
-15
lines changed

3 files changed

+74
-15
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
- Added `is_bigraphical()` to test whether a pair of integer sequences can be the degree sequence of some bipartite graph.
88

9+
- Added `weights=...` keyword argument to `Graph.radius()` and `Graph.eccentricity()`.
10+
911
## [0.10.7] - 2023-09-04
1012

1113
### Added

src/_igraph/graphobject.c

+51-15
Original file line numberDiff line numberDiff line change
@@ -1869,24 +1869,37 @@ PyObject *igraphmodule_Graph_knn(igraphmodule_GraphObject *self,
18691869
PyObject *igraphmodule_Graph_radius(igraphmodule_GraphObject * self,
18701870
PyObject * args, PyObject * kwds)
18711871
{
1872-
PyObject *mode_o = Py_None;
1872+
PyObject *mode_o = Py_None, *weights_o = Py_None;
18731873
igraph_neimode_t mode = IGRAPH_OUT;
18741874
igraph_real_t radius;
1875+
igraph_vector_t *weights;
18751876

1876-
static char *kwlist[] = { "mode", NULL };
1877+
static char *kwlist[] = { "mode", "weights", NULL };
18771878

1878-
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist,
1879-
&mode_o))
1879+
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, &mode_o, &weights_o)) {
18801880
return NULL;
1881+
}
18811882

1882-
if (igraphmodule_PyObject_to_neimode_t(mode_o, &mode))
1883+
if (igraphmodule_PyObject_to_neimode_t(mode_o, &mode)) {
18831884
return NULL;
1885+
}
18841886

1885-
if (igraph_radius(&self->g, &radius, mode)) {
1887+
if (igraphmodule_attrib_to_vector_t(weights_o, self, &weights, ATTRIBUTE_TYPE_EDGE)) {
1888+
return NULL;
1889+
}
1890+
1891+
if (igraph_radius_dijkstra(&self->g, weights, &radius, mode)) {
1892+
if (weights) {
1893+
igraph_vector_destroy(weights); free(weights);
1894+
}
18861895
igraphmodule_handle_igraph_error();
18871896
return NULL;
18881897
}
18891898

1899+
if (weights) {
1900+
igraph_vector_destroy(weights); free(weights);
1901+
}
1902+
18901903
return igraphmodule_real_t_to_PyObject(radius, IGRAPHMODULE_TYPE_FLOAT_IF_FRACTIONAL_ELSE_INT);
18911904
}
18921905

@@ -4862,18 +4875,21 @@ PyObject *igraphmodule_Graph_decompose(igraphmodule_GraphObject * self,
48624875
*/
48634876
PyObject *igraphmodule_Graph_eccentricity(igraphmodule_GraphObject* self,
48644877
PyObject* args, PyObject* kwds) {
4865-
static char *kwlist[] = { "vertices", "mode", NULL };
4866-
PyObject *vobj = Py_None, *list = NULL, *mode_o = Py_None;
4878+
static char *kwlist[] = { "vertices", "mode", "weights", NULL };
4879+
PyObject *vobj = Py_None, *list = NULL, *mode_o = Py_None, *weights_o = Py_None;
48674880
igraph_vector_t res;
48684881
igraph_neimode_t mode = IGRAPH_OUT;
48694882
igraph_bool_t return_single = false;
48704883
igraph_vs_t vs;
4884+
igraph_vector_t* weights;
48714885

4872-
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, &vobj, &mode_o))
4886+
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO", kwlist, &vobj, &mode_o, &weights_o)) {
48734887
return NULL;
4888+
}
48744889

4875-
if (igraphmodule_PyObject_to_neimode_t(mode_o, &mode))
4890+
if (igraphmodule_PyObject_to_neimode_t(mode_o, &mode)) {
48764891
return NULL;
4892+
}
48774893

48784894
if (igraphmodule_PyObject_to_vs_t(vobj, &vs, &self->g, &return_single, 0)) {
48794895
igraphmodule_handle_igraph_error();
@@ -4885,17 +4901,31 @@ PyObject *igraphmodule_Graph_eccentricity(igraphmodule_GraphObject* self,
48854901
return igraphmodule_handle_igraph_error();
48864902
}
48874903

4888-
if (igraph_eccentricity(&self->g, &res, vs, mode)) {
4904+
if (igraphmodule_attrib_to_vector_t(weights_o, self, &weights, ATTRIBUTE_TYPE_EDGE)) {
4905+
igraph_vs_destroy(&vs);
4906+
igraph_vector_destroy(&res);
4907+
return NULL;
4908+
}
4909+
4910+
if (igraph_eccentricity_dijkstra(&self->g, weights, &res, vs, mode)) {
4911+
if (weights) {
4912+
igraph_vector_destroy(weights); free(weights);
4913+
}
48894914
igraph_vs_destroy(&vs);
48904915
igraph_vector_destroy(&res);
48914916
igraphmodule_handle_igraph_error();
48924917
return NULL;
48934918
}
48944919

4895-
if (!return_single)
4920+
if (weights) {
4921+
igraph_vector_destroy(weights); free(weights);
4922+
}
4923+
4924+
if (!return_single) {
48964925
list = igraphmodule_vector_t_to_PyList(&res, IGRAPHMODULE_TYPE_FLOAT);
4897-
else
4926+
} else {
48984927
list = PyFloat_FromDouble(VECTOR(res)[0]);
4928+
}
48994929

49004930
igraph_vector_destroy(&res);
49014931
igraph_vs_destroy(&vs);
@@ -14786,7 +14816,7 @@ struct PyMethodDef igraphmodule_Graph_methods[] = {
1478614816
/* interface to igraph_eccentricity */
1478714817
{"eccentricity", (PyCFunction) igraphmodule_Graph_eccentricity,
1478814818
METH_VARARGS | METH_KEYWORDS,
14789-
"eccentricity(vertices=None, mode=\"all\")\n--\n\n"
14819+
"eccentricity(vertices=None, mode=\"all\", weights=None)\n--\n\n"
1479014820
"Calculates the eccentricities of given vertices in a graph.\n\n"
1479114821
"The eccentricity of a vertex is calculated by measuring the\n"
1479214822
"shortest distance from (or to) the vertex, to (or from) all other\n"
@@ -14797,6 +14827,9 @@ struct PyMethodDef igraphmodule_Graph_methods[] = {
1479714827
" that edge directions are followed; C{\"out\"} means that edge directions\n"
1479814828
" are followed the opposite direction; C{\"all\"} means that directions are\n"
1479914829
" ignored. The argument has no effect for undirected graphs.\n"
14830+
"@param weights: a list containing the edge weights. It can also be\n"
14831+
" an attribute name (edge weights are retrieved from the given\n"
14832+
" attribute) or C{None} (all edges have equal weight).\n"
1480014833
"@return: the calculated eccentricities in a list, or a single number if\n"
1480114834
" a single vertex was supplied.\n"},
1480214835

@@ -15358,7 +15391,7 @@ struct PyMethodDef igraphmodule_Graph_methods[] = {
1535815391
/* interfaces to igraph_radius */
1535915392
{"radius", (PyCFunction) igraphmodule_Graph_radius,
1536015393
METH_VARARGS | METH_KEYWORDS,
15361-
"radius(mode=\"out\")\n--\n\n"
15394+
"radius(mode=\"out\", weights=None)\n--\n\n"
1536215395
"Calculates the radius of the graph.\n\n"
1536315396
"The radius of a graph is defined as the minimum eccentricity of\n"
1536415397
"its vertices (see L{eccentricity()}).\n"
@@ -15367,6 +15400,9 @@ struct PyMethodDef igraphmodule_Graph_methods[] = {
1536715400
" edge directions, C{IN} considers paths that follow the opposite\n"
1536815401
" edge directions, C{ALL} ignores edge directions. The argument is\n"
1536915402
" ignored for undirected graphs.\n"
15403+
"@param weights: a list containing the edge weights. It can also be\n"
15404+
" an attribute name (edge weights are retrieved from the given\n"
15405+
" attribute) or C{None} (all edges have equal weight).\n"
1537015406
"@return: the radius\n"
1537115407
"@see: L{eccentricity()}"
1537215408
},

tests/test_structural.py

+21
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,21 @@ def testEccentricity(self):
5656
)
5757
self.assertEqual(Graph().eccentricity(), [])
5858

59+
def testWeightedEccentricity(self):
60+
self.assertEqual(
61+
self.gfull.eccentricity(weights=[2] * self.gfull.ecount()),
62+
[2] * self.gfull.vcount()
63+
)
64+
self.assertEqual(
65+
self.gempty.eccentricity(weights=[]),
66+
[0] * self.gempty.vcount()
67+
)
68+
self.assertEqual(
69+
self.g.eccentricity(weights=range(1, self.g.ecount() + 1)),
70+
[4, 5, 6, 6]
71+
)
72+
self.assertEqual(Graph().eccentricity(), [])
73+
5974
def testRadius(self):
6075
self.assertEqual(self.gfull.radius(), 1)
6176
self.assertEqual(self.gempty.radius(), 0)
@@ -64,6 +79,12 @@ def testRadius(self):
6479
self.assertEqual(self.tree.radius(), 3)
6580
self.assertTrue(isnan(Graph().radius()))
6681

82+
def testWeightedRadius(self):
83+
self.assertEqual(self.gfull.radius(weights=[2] * self.gfull.ecount()), 2)
84+
self.assertEqual(self.gempty.radius(weights=[]), 0)
85+
self.assertEqual(self.g.radius(weights=range(1, self.g.ecount() + 1)), 4)
86+
self.assertTrue(isnan(Graph().radius()))
87+
6788
def testTransitivity(self):
6889
self.assertTrue(self.gfull.transitivity_undirected() == 1.0)
6990
self.assertTrue(self.tree.transitivity_undirected() == 0.0)

0 commit comments

Comments
 (0)