diff --git a/src/_igraph/convert.c b/src/_igraph/convert.c index c05a4eba2..82201f553 100644 --- a/src/_igraph/convert.c +++ b/src/_igraph/convert.c @@ -2594,10 +2594,10 @@ PyObject* igraphmodule_graph_list_t_to_PyList(igraph_graph_list_t *v, PyTypeObje * applicable. May be used in error messages. * \return 0 if everything was OK, 1 otherwise. Sets appropriate exceptions. */ -int igraphmodule_PyList_to_matrix_t( +int igraphmodule_PyObject_to_matrix_t( PyObject* o, igraph_matrix_t *m, const char *arg_name ) { - return igraphmodule_PyList_to_matrix_t_with_minimum_column_count(o, m, 0, arg_name); + return igraphmodule_PyObject_to_matrix_t_with_minimum_column_count(o, m, 0, arg_name); } /** @@ -2612,7 +2612,7 @@ int igraphmodule_PyList_to_matrix_t( * applicable. May be used in error messages. * \return 0 if everything was OK, 1 otherwise. Sets appropriate exceptions. */ -int igraphmodule_PyList_to_matrix_t_with_minimum_column_count( +int igraphmodule_PyObject_to_matrix_t_with_minimum_column_count( PyObject *o, igraph_matrix_t *m, int min_cols, const char *arg_name ) { Py_ssize_t nr, nc, n, i, j; @@ -2630,6 +2630,10 @@ int igraphmodule_PyList_to_matrix_t_with_minimum_column_count( } nr = PySequence_Size(o); + if (nr < 0) { + return 1; + } + nc = min_cols > 0 ? min_cols : 0; for (i = 0; i < nr; i++) { row = PySequence_GetItem(o, i); @@ -2644,18 +2648,30 @@ int igraphmodule_PyList_to_matrix_t_with_minimum_column_count( } n = PySequence_Size(row); Py_DECREF(row); + if (n < 0) { + return 1; + } if (n > nc) { nc = n; } } - igraph_matrix_init(m, nr, nc); + if (igraph_matrix_init(m, nr, nc)) { + igraphmodule_handle_igraph_error(); + return 1; + } + for (i = 0; i < nr; i++) { row = PySequence_GetItem(o, i); n = PySequence_Size(row); for (j = 0; j < n; j++) { item = PySequence_GetItem(row, j); + if (!item) { + igraph_matrix_destroy(m); + return 1; + } if (igraphmodule_PyObject_to_real_t(item, &value)) { + igraph_matrix_destroy(m); Py_DECREF(item); return 1; } @@ -2678,10 +2694,10 @@ int igraphmodule_PyList_to_matrix_t_with_minimum_column_count( * applicable. May be used in error messages. * \return 0 if everything was OK, 1 otherwise. Sets appropriate exceptions. */ -int igraphmodule_PyList_to_matrix_int_t( +int igraphmodule_PyObject_to_matrix_int_t( PyObject* o, igraph_matrix_int_t *m, const char* arg_name ) { - return igraphmodule_PyList_to_matrix_int_t_with_minimum_column_count(o, m, 0, arg_name); + return igraphmodule_PyObject_to_matrix_int_t_with_minimum_column_count(o, m, 0, arg_name); } /** @@ -2696,7 +2712,7 @@ int igraphmodule_PyList_to_matrix_int_t( * applicable. May be used in error messages. * \return 0 if everything was OK, 1 otherwise. Sets appropriate exceptions. */ -int igraphmodule_PyList_to_matrix_int_t_with_minimum_column_count( +int igraphmodule_PyObject_to_matrix_int_t_with_minimum_column_count( PyObject *o, igraph_matrix_int_t *m, int min_cols, const char* arg_name ) { Py_ssize_t nr, nc, n, i, j; @@ -2714,6 +2730,10 @@ int igraphmodule_PyList_to_matrix_int_t_with_minimum_column_count( } nr = PySequence_Size(o); + if (nr < 0) { + return 1; + } + nc = min_cols > 0 ? min_cols : 0; for (i = 0; i < nr; i++) { row = PySequence_GetItem(o, i); @@ -2728,6 +2748,9 @@ int igraphmodule_PyList_to_matrix_int_t_with_minimum_column_count( } n = PySequence_Size(row); Py_DECREF(row); + if (n < 0) { + return 1; + } if (n > nc) { nc = n; } @@ -2743,7 +2766,12 @@ int igraphmodule_PyList_to_matrix_int_t_with_minimum_column_count( n = PySequence_Size(row); for (j = 0; j < n; j++) { item = PySequence_GetItem(row, j); + if (!item) { + igraph_matrix_int_destroy(m); + return 1; + } if (igraphmodule_PyObject_to_integer_t(item, &value)) { + igraph_matrix_int_destroy(m); Py_DECREF(item); return 1; } diff --git a/src/_igraph/convert.h b/src/_igraph/convert.h index 006f0b652..3b35d2459 100644 --- a/src/_igraph/convert.h +++ b/src/_igraph/convert.h @@ -116,10 +116,15 @@ int igraphmodule_PyObject_to_edgelist( igraph_bool_t *list_is_owned ); -int igraphmodule_PyList_to_matrix_t(PyObject *o, igraph_matrix_t *m, const char *arg_name); -int igraphmodule_PyList_to_matrix_t_with_minimum_column_count(PyObject *o, igraph_matrix_t *m, int min_cols, const char *arg_name); -int igraphmodule_PyList_to_matrix_int_t(PyObject *o, igraph_matrix_int_t *m, const char *arg_name); -int igraphmodule_PyList_to_matrix_int_t_with_minimum_column_count(PyObject *o, igraph_matrix_int_t *m, int min_cols, const char *arg_name); +int igraphmodule_PyObject_to_matrix_t( + PyObject *o, igraph_matrix_t *m, const char *arg_name); +int igraphmodule_PyObject_to_matrix_t_with_minimum_column_count( + PyObject *o, igraph_matrix_t *m, int min_cols, const char *arg_name); +int igraphmodule_PyObject_to_matrix_int_t( + PyObject *o, igraph_matrix_int_t *m, const char *arg_name); +int igraphmodule_PyObject_to_matrix_int_t_with_minimum_column_count( + PyObject *o, igraph_matrix_int_t *m, int min_cols, const char *arg_name); + PyObject* igraphmodule_strvector_t_to_PyList(igraph_strvector_t *v); int igraphmodule_PyList_to_strvector_t(PyObject* v, igraph_strvector_t *result); int igraphmodule_PyList_to_existing_strvector_t(PyObject* v, igraph_strvector_t *result); diff --git a/src/_igraph/graphobject.c b/src/_igraph/graphobject.c index dc9161b3e..54e733817 100644 --- a/src/_igraph/graphobject.c +++ b/src/_igraph/graphobject.c @@ -1943,14 +1943,14 @@ PyObject *igraphmodule_Graph_Adjacency(PyTypeObject * type, igraphmodule_GraphObject *self; igraph_t g; igraph_matrix_t m; - PyObject *matrix, *mode_o = Py_None, *loops_o = Py_None; + PyObject *matrix_o, *mode_o = Py_None, *loops_o = Py_None; igraph_adjacency_t mode = IGRAPH_ADJ_DIRECTED; igraph_loops_t loops = IGRAPH_LOOPS_ONCE; static char *kwlist[] = { "matrix", "mode", "loops", NULL }; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|OO", kwlist, - &PyList_Type, &matrix, &mode_o, &loops_o)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OO", kwlist, + &matrix_o, &mode_o, &loops_o)) return NULL; if (igraphmodule_PyObject_to_adjacency_t(mode_o, &mode)) @@ -1959,7 +1959,7 @@ PyObject *igraphmodule_Graph_Adjacency(PyTypeObject * type, if (igraphmodule_PyObject_to_loops_t(loops_o, &loops)) return NULL; - if (igraphmodule_PyList_to_matrix_t(matrix, &m, "matrix")) { + if (igraphmodule_PyObject_to_matrix_t(matrix_o, &m, "matrix")) { return NULL; } @@ -2281,9 +2281,8 @@ PyObject *igraphmodule_Graph_Establishment(PyTypeObject * type, char *kwlist[] = { "n", "k", "type_dist", "pref_matrix", "directed", NULL }; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "nnO!O!|O", kwlist, - &n, &k, &PyList_Type, &type_dist, - &PyList_Type, &pref_matrix, &directed)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "nnOO|O", kwlist, + &n, &k, &type_dist, &pref_matrix, &directed)) return NULL; if (n <= 0 || k <= 0) { @@ -2295,29 +2294,32 @@ PyObject *igraphmodule_Graph_Establishment(PyTypeObject * type, CHECK_SSIZE_T_RANGE(n, "vertex count"); CHECK_SSIZE_T_RANGE(k, "connection trials per set"); - types = PyList_Size(type_dist); - - if (igraphmodule_PyList_to_matrix_t(pref_matrix, &pm, "pref_matrix")) { + if (igraphmodule_PyObject_to_vector_t(type_dist, &td, 1)) { + PyErr_SetString(PyExc_ValueError, + "Error while converting type distribution vector"); return NULL; } + + if (igraphmodule_PyObject_to_matrix_t(pref_matrix, &pm, "pref_matrix")) { + igraph_vector_destroy(&td); + return NULL; + } + + types = igraph_vector_size(&td); + if (igraph_matrix_nrow(&pm) != igraph_matrix_ncol(&pm) || igraph_matrix_nrow(&pm) != types) { PyErr_SetString(PyExc_ValueError, "Preference matrix must have exactly the same rows and columns as the number of types"); - igraph_matrix_destroy(&pm); - return NULL; - } - if (igraphmodule_PyObject_to_vector_t(type_dist, &td, 1)) { - PyErr_SetString(PyExc_ValueError, - "Error while converting type distribution vector"); + igraph_vector_destroy(&td); igraph_matrix_destroy(&pm); return NULL; } if (igraph_establishment_game(&g, n, types, k, &td, &pm, PyObject_IsTrue(directed), 0)) { igraphmodule_handle_igraph_error(); - igraph_matrix_destroy(&pm); igraph_vector_destroy(&td); + igraph_matrix_destroy(&pm); return NULL; } @@ -2659,7 +2661,7 @@ PyObject *igraphmodule_Graph_Biadjacency(PyTypeObject * type, static char *kwlist[] = { "matrix", "directed", "mode", "multiple", NULL }; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|OOO", kwlist, &PyList_Type, &matrix_o, + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOO", kwlist, &matrix_o, &directed, &mode_o, &multiple)) return NULL; @@ -2670,7 +2672,7 @@ PyObject *igraphmodule_Graph_Biadjacency(PyTypeObject * type, return NULL; } - if (igraphmodule_PyList_to_matrix_t(matrix_o, &matrix, "matrix")) { + if (igraphmodule_PyObject_to_matrix_t(matrix_o, &matrix, "matrix")) { igraph_vector_bool_destroy(&vertex_types); return NULL; } @@ -2977,16 +2979,15 @@ PyObject *igraphmodule_Graph_Preference(PyTypeObject * type, { "n", "type_dist", "pref_matrix", "attribute", "directed", "loops", NULL }; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "nO!O!|OOO", kwlist, - &n, &PyList_Type, &type_dist, - &PyList_Type, &pref_matrix, + if (!PyArg_ParseTupleAndKeywords(args, kwds, "nOO|OOO", kwlist, + &n, &type_dist, &pref_matrix, &attribute_key, &directed, &loops)) return NULL; CHECK_SSIZE_T_RANGE(n, "vertex count"); types = PyList_Size(type_dist); - if (igraphmodule_PyList_to_matrix_t(pref_matrix, &pm, "pref_matrix")) { + if (igraphmodule_PyObject_to_matrix_t(pref_matrix, &pm, "pref_matrix")) { return NULL; } if (igraphmodule_PyObject_float_to_vector_t(type_dist, &td)) { @@ -3070,18 +3071,18 @@ PyObject *igraphmodule_Graph_Asymmetric_Preference(PyTypeObject * type, char *kwlist[] = { "n", "type_dist_matrix", "pref_matrix", "attribute", "loops", NULL }; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "nO!O!|OO", kwlist, - &n, &PyList_Type, &type_dist_matrix, - &PyList_Type, &pref_matrix, + if (!PyArg_ParseTupleAndKeywords(args, kwds, "nOO|OO", kwlist, + &n, &type_dist_matrix, + &pref_matrix, &attribute_key, &loops)) return NULL; CHECK_SSIZE_T_RANGE(n, "vertex count"); - if (igraphmodule_PyList_to_matrix_t(pref_matrix, &pm, "pref_matrix")) { + if (igraphmodule_PyObject_to_matrix_t(pref_matrix, &pm, "pref_matrix")) { return NULL; } - if (igraphmodule_PyList_to_matrix_t(type_dist_matrix, &td, "type_dist_matrix")) { + if (igraphmodule_PyObject_to_matrix_t(type_dist_matrix, &td, "type_dist_matrix")) { igraph_matrix_destroy(&pm); return NULL; } @@ -3375,15 +3376,15 @@ PyObject *igraphmodule_Graph_SBM(PyTypeObject * type, static char *kwlist[] = { "n", "pref_matrix", "block_sizes", "directed", "loops", NULL }; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "nO!O!|OO", kwlist, - &n, &PyList_Type, &pref_matrix_o, - &PyList_Type, &block_sizes_o, + if (!PyArg_ParseTupleAndKeywords(args, kwds, "nOO|OO", kwlist, + &n, &pref_matrix_o, + &block_sizes_o, &directed_o, &loops_o)) return NULL; CHECK_SSIZE_T_RANGE(n, "vertex count"); - if (igraphmodule_PyList_to_matrix_t(pref_matrix_o, &pref_matrix, "pref_matrix")) { + if (igraphmodule_PyObject_to_matrix_t(pref_matrix_o, &pref_matrix, "pref_matrix")) { return NULL; } @@ -3746,8 +3747,8 @@ PyObject *igraphmodule_Graph_Weighted_Adjacency(PyTypeObject * type, static char *kwlist[] = { "matrix", "mode", "loops", NULL }; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|OO", kwlist, - &PyList_Type, &matrix, &mode_o, &loops_o)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OO", kwlist, + &matrix, &mode_o, &loops_o)) return NULL; if (igraphmodule_PyObject_to_adjacency_t(mode_o, &mode)) @@ -3760,7 +3761,7 @@ PyObject *igraphmodule_Graph_Weighted_Adjacency(PyTypeObject * type, } else if (igraphmodule_PyObject_to_loops_t(loops_o, &loops)) return NULL; - if (igraphmodule_PyList_to_matrix_t(matrix, &m, "matrix")) { + if (igraphmodule_PyObject_to_matrix_t(matrix, &m, "matrix")) { return NULL; } @@ -6234,7 +6235,7 @@ PyObject *igraphmodule_Graph_permute_vertices(igraphmodule_GraphObject *self, igraphmodule_GraphObject *result_o; PyObject *list; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!", kwlist, &PyList_Type, &list)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &list)) return NULL; if (igraphmodule_PyObject_to_vector_int_t(list, &perm)) @@ -7790,7 +7791,7 @@ PyObject *igraphmodule_Graph_layout_kamada_kawai(igraphmodule_GraphObject * } } else { use_seed = 1; - if (igraphmodule_PyList_to_matrix_t(seed_o, &m, "seed")) { + if (igraphmodule_PyObject_to_matrix_t(seed_o, &m, "seed")) { return NULL; } } @@ -7936,7 +7937,7 @@ PyObject* igraphmodule_Graph_layout_davidson_harel(igraphmodule_GraphObject *sel return NULL; } } else { - if (igraphmodule_PyList_to_matrix_t(seed_o, &m, "seed")) { + if (igraphmodule_PyObject_to_matrix_t(seed_o, &m, "seed")) { return NULL; } use_seed = 1; @@ -8005,7 +8006,7 @@ PyObject* igraphmodule_Graph_layout_drl(igraphmodule_GraphObject *self, return NULL; } } else { - if (igraphmodule_PyList_to_matrix_t(seed_o, &m, "seed")) { + if (igraphmodule_PyObject_to_matrix_t(seed_o, &m, "seed")) { return NULL; } use_seed = 1; @@ -8104,7 +8105,7 @@ PyObject return NULL; } } else { - if (igraphmodule_PyList_to_matrix_t(seed_o, &m, "seed")) { + if (igraphmodule_PyObject_to_matrix_t(seed_o, &m, "seed")) { return NULL; } use_seed = 1; @@ -8215,7 +8216,7 @@ PyObject *igraphmodule_Graph_layout_graphopt(igraphmodule_GraphObject *self, } } else { use_seed = 1; - if (igraphmodule_PyList_to_matrix_t(seed_o, &m, "seed")) { + if (igraphmodule_PyObject_to_matrix_t(seed_o, &m, "seed")) { return NULL; } } @@ -8324,7 +8325,7 @@ PyObject *igraphmodule_Graph_layout_mds(igraphmodule_GraphObject * self, PyErr_NoMemory(); return NULL; } - if (igraphmodule_PyList_to_matrix_t(dist_o, dist, "dist")) { + if (igraphmodule_PyObject_to_matrix_t(dist_o, dist, "dist")) { free(dist); return NULL; } @@ -8614,7 +8615,7 @@ PyObject *igraphmodule_Graph_layout_umap( } } else { use_seed = 1; - if (igraphmodule_PyList_to_matrix_t(seed_o, &m, "seed")) { + if (igraphmodule_PyObject_to_matrix_t(seed_o, &m, "seed")) { return NULL; } } @@ -9859,7 +9860,7 @@ PyObject *igraphmodule_Graph_isoclass(igraphmodule_GraphObject * self, char *kwlist[] = { "vertices", NULL }; if (!PyArg_ParseTupleAndKeywords - (args, kwds, "|O!", kwlist, &PyList_Type, &vids)) + (args, kwds, "|O", kwlist, &vids)) return NULL; if (vids) { diff --git a/src/_igraph/igraphmodule.c b/src/_igraph/igraphmodule.c index cff5df44e..97864f61e 100644 --- a/src/_igraph/igraphmodule.c +++ b/src/_igraph/igraphmodule.c @@ -355,10 +355,10 @@ PyObject* igraphmodule_community_to_membership(PyObject *self, igraph_vector_int_t result, csize, *csize_p = 0; Py_ssize_t nodes, steps; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!nn|O", kwlist, - &PyList_Type, &merges_o, &nodes, &steps, &return_csize)) return NULL; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "Onn|O", kwlist, + &merges_o, &nodes, &steps, &return_csize)) return NULL; - if (igraphmodule_PyList_to_matrix_int_t_with_minimum_column_count(merges_o, &merges, 2, "merges")) { + if (igraphmodule_PyObject_to_matrix_int_t_with_minimum_column_count(merges_o, &merges, 2, "merges")) { return NULL; } diff --git a/src/igraph/datatypes.py b/src/igraph/datatypes.py index a8f26ed4c..a2446802d 100644 --- a/src/igraph/datatypes.py +++ b/src/igraph/datatypes.py @@ -184,6 +184,10 @@ def __isub__(self, other): for i in range(len(row)): row[i] -= other return self + + def __len__(self): + """Returns the number of rows in the matrix.""" + return len(self._data) def __ne__(self, other): """Checks whether a given matrix is not equal to another one""" diff --git a/tests/test_conversion.py b/tests/test_conversion.py index 394f07cc7..bbd9574c5 100644 --- a/tests/test_conversion.py +++ b/tests/test_conversion.py @@ -171,6 +171,14 @@ def testGetSparseAdjacency(self): np.all(g.get_adjacency_sparse() == np.array(g.get_adjacency().data)) ) + def testGetAdjacencyRoundtrip(self): + g = Graph.Tree(6, 3) + adj = g.get_adjacency() + g2 = Graph.Adjacency(adj, mode="undirected") + self.assertEqual(g.vcount(), g2.vcount()) + self.assertEqual(g.is_directed(), g2.is_directed()) + self.assertTrue(g.get_edgelist() == g2.get_edgelist()) + class PruferTests(unittest.TestCase): def testFromPrufer(self):