1
/* vim:set ts=2 sw=2 sts=2 et: */
4
Copyright (C) 2006 Gabor Csardi <csardi@rmki.kfki.hu>
5
MTA RMKI, Konkoly-Thege Miklos st. 29-33, Budapest 1121, Hungary
7
This program is free software; you can redistribute it and/or modify
8
it under the terms of the GNU General Public License as published by
9
the Free Software Foundation; either version 2 of the License, or
10
(at your option) any later version.
12
This program is distributed in the hope that it will be useful,
13
but WITHOUT ANY WARRANTY; without even the implied warranty of
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
GNU General Public License for more details.
17
You should have received a copy of the GNU General Public License
18
along with this program; if not, write to the Free Software
19
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25
#include <pythonrun.h>
26
#include <igraph/igraph.h>
30
#include "graphobject.h"
31
#include "vertexseqobject.h"
32
#include "vertexobject.h"
33
#include "edgeseqobject.h"
34
#include "edgeobject.h"
35
#include "arpackobject.h"
39
extern double igraph_i_fdiv(double, double);
42
* \defgroup python_interface Python module implementation
43
* \brief Functions implementing a Python interface to \a igraph
45
* These functions provide a way to access \a igraph functions from Python.
46
* It should be of interest of \a igraph developers only. Classes, functions
47
* and methods exposed to Python are still to be documented. Until it is done,
48
* just type the following to get help about \a igraph functions in Python
49
* (assuming you have \c igraph.so somewhere in your Python library path):
57
* Most of the functions provided here share the same calling conventions
58
* (which are determined by the Python/C API). Since the role of the
59
* arguments are the same across many functions, I won't explain them
60
* everywhere, just give a quick overview of the common argument names here.
62
* \param self the Python igraph.Graph object the method is working on
63
* \param args pointer to the Python tuple containing the arguments
64
* \param kwds pointer to the Python hash containing the keyword parameters
65
* \param type the type object of a Python igraph.Graph object. Used usually
66
* in constructors and class methods.
68
* Any arguments not documented here should be mentioned at the documentation
69
* of the appropriate method.
71
* The functions which implement a Python method always return a pointer to
72
* a \c PyObject. According to Python conventions, this is \c NULL if and
73
* only if an exception was thrown by the method (or any of the functions
74
* it has called). When I explain the return value of a function which
75
* provides interface to an \a igraph function, I won't cover the case of
76
* returning a \c NULL value, because this is the same for every such method.
77
* The conclusion is that a method can return \c NULL even if I don't state
80
* Also please take into consideration that I'm documenting the C calls
81
* with the abovementioned parameters here, and \em not the Python methods
82
* which are presented to the user using the Python interface of \a igraph.
83
* If you are looking for the documentation of the classes, methods and
84
* functions exposed to Python, please use the \c help calls from Python
85
* or use \c pydoc to generate a formatted version.
87
* \section weakrefs The usage of weak references in the Python interface
89
* Many classes implemented in the Python interface (e.g. VertexSeq, Vertex...)
90
* use weak references to keep track of the graph they are referencing to.
91
* The use of weak references is twofold:
93
* -# If we assign a VertexSeq or a Vertex of a given graph to a local
94
* variable and then destroy the graph, real references keep the graph
95
* alive and do not return the memory back to Python.
96
* -# If we use real references, a Graph object will hold a reference
97
* to its VertexSeq (because we don't want to allocate a new VertexSeq
98
* object for the same graph every time it is requested), and the
99
* VertexSeq will also hold a reference to the Graph. This is a circular
100
* reference. Python does not reclaim the memory occupied by the Graph
101
* back when the Graph is destroyed, because the VertexSeq is holding a
102
* reference to it. Similarly, VertexSeq doesn't get freed because the
103
* Graph is holding a reference to it. These situations can only be
104
* resolved by the Python garbage collector which is invoked at regular
105
* intervals. Unfortunately, the garbage collector refuses to break
106
* circular references and free the objects participating in the circle
107
* when any of the objects has a \c __del__ method. In this case,
108
* \c igraph.Graph has one (which frees the underlying \c igraph_t
109
* graph), therefore our graphs never get freed when we use real
113
static PyObject* igraphmodule_progress_handler=NULL;
115
static int igraphmodule_igraph_interrupt_hook(void* data) {
116
if (PyErr_CheckSignals()) {
117
IGRAPH_FINALLY_FREE();
118
return IGRAPH_INTERRUPTED;
120
return IGRAPH_SUCCESS;
123
int igraphmodule_igraph_progress_hook(const char* message, igraph_real_t percent,
125
if (igraphmodule_progress_handler) {
127
if (PyCallable_Check(igraphmodule_progress_handler)) {
128
result=PyObject_CallFunction(igraphmodule_progress_handler,
129
"sd", message, (double)percent);
130
if (result) Py_DECREF(result);
131
else return IGRAPH_INTERRUPTED;
135
return IGRAPH_SUCCESS;
138
PyObject* igraphmodule_set_progress_handler(PyObject* self, PyObject* args) {
140
if (!PyArg_ParseTuple(args, "O", &o)) return NULL;
141
if (!PyCallable_Check(o) && o != Py_None) {
142
PyErr_SetString(PyExc_TypeError, "Progress handler must be callable.");
145
Py_XDECREF(igraphmodule_progress_handler);
147
igraphmodule_progress_handler=NULL;
150
igraphmodule_progress_handler=o;
156
PyObject* igraphmodule_convex_hull(PyObject* self, PyObject* args, PyObject* kwds) {
157
static char* kwlist[] = {"vs", "coords", NULL};
158
PyObject *vs, *o, *o1=0, *o2=0, *coords = Py_False;
159
igraph_matrix_t mtrx;
160
igraph_vector_t result;
161
igraph_matrix_t resmat;
164
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|O", kwlist, &PyList_Type, &vs, &coords))
167
no_of_nodes=PyList_Size(vs);
168
if (igraph_matrix_init(&mtrx, no_of_nodes, 2)) {
169
igraphmodule_handle_igraph_error();
172
for (i=0; i<no_of_nodes; i++) {
173
o=PyList_GetItem(vs, i);
174
if (PyList_Check(o)) {
175
if (PyList_Size(o) >= 2) {
176
o1=PyList_GetItem(o, 0);
177
o2=PyList_GetItem(o, 1);
178
if (PyList_Size(o) > 2)
179
PyErr_Warn(PyExc_Warning, "vertex with more than 2 coordinates found, considering only the first 2");
181
PyErr_SetString(PyExc_TypeError, "vertex with less than 2 coordinates found");
182
igraph_matrix_destroy(&mtrx);
185
} else if (PyTuple_Check(o)) {
186
if (PyTuple_Size(o) >= 2) {
187
o1=PyTuple_GetItem(o, 0);
188
o2=PyTuple_GetItem(o, 1);
189
if (PyTuple_Size(o) > 2)
190
PyErr_Warn(PyExc_Warning, "vertex with more than 2 coordinates found, considering only the first 2");
192
PyErr_SetString(PyExc_TypeError, "vertex with less than 2 coordinates found");
193
igraph_matrix_destroy(&mtrx);
198
if (!PyNumber_Check(o1) || !PyNumber_Check(o2)) {
199
PyErr_SetString(PyExc_TypeError, "vertex coordinates must be numeric");
200
igraph_matrix_destroy(&mtrx);
203
/* o, o1 and o2 were borrowed, but now o1 and o2 are actual references! */
204
o1=PyNumber_Float(o1); o2=PyNumber_Float(o2);
206
PyErr_SetString(PyExc_TypeError, "vertex coordinate conversion to float failed");
209
igraph_matrix_destroy(&mtrx);
212
MATRIX(mtrx, i, 0)=(igraph_real_t)PyFloat_AsDouble(o1);
213
MATRIX(mtrx, i, 1)=(igraph_real_t)PyFloat_AsDouble(o2);
218
if (!PyObject_IsTrue(coords)) {
219
if (igraph_vector_init(&result, 0)) {
220
igraphmodule_handle_igraph_error();
221
igraph_matrix_destroy(&mtrx);
224
if (igraph_convex_hull(&mtrx, &result, 0)) {
225
igraphmodule_handle_igraph_error();
226
igraph_matrix_destroy(&mtrx);
227
igraph_vector_destroy(&result);
230
o=igraphmodule_vector_t_to_PyList(&result, IGRAPHMODULE_TYPE_INT);
231
igraph_vector_destroy(&result);
233
if (igraph_matrix_init(&resmat, 0, 0)) {
234
igraphmodule_handle_igraph_error();
235
igraph_matrix_destroy(&mtrx);
238
if (igraph_convex_hull(&mtrx, 0, &resmat)) {
239
igraphmodule_handle_igraph_error();
240
igraph_matrix_destroy(&mtrx);
241
igraph_matrix_destroy(&resmat);
244
o=igraphmodule_matrix_t_to_PyList(&resmat, IGRAPHMODULE_TYPE_FLOAT);
245
igraph_matrix_destroy(&resmat);
248
igraph_matrix_destroy(&mtrx);
254
PyObject* igraphmodule_community_to_membership(PyObject *self,
255
PyObject *args, PyObject *kwds) {
256
static char* kwlist[] = { "merges", "nodes", "steps", "return_csize", NULL };
257
PyObject *merges_o, *return_csize = Py_False, *result_o;
258
igraph_matrix_t merges;
259
igraph_vector_t result, csize, *csize_p = 0;
260
long int nodes, steps;
262
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!ll|O", kwlist,
263
&PyList_Type, &merges_o, &nodes, &steps, &return_csize)) return NULL;
265
if (igraphmodule_PyList_to_matrix_t(merges_o, &merges)) return NULL;
267
if (igraph_vector_init(&result, nodes)) {
268
igraphmodule_handle_igraph_error();
269
igraph_matrix_destroy(&merges);
273
if (PyObject_IsTrue(return_csize)) {
274
igraph_vector_init(&csize, 0);
278
if (igraph_community_to_membership(&merges, nodes, steps, &result, csize_p)) {
279
igraphmodule_handle_igraph_error();
280
igraph_vector_destroy(&result);
281
if (csize_p) igraph_vector_destroy(csize_p);
282
igraph_matrix_destroy(&merges);
285
igraph_matrix_destroy(&merges);
287
result_o = igraphmodule_vector_t_to_PyList(&result, IGRAPHMODULE_TYPE_INT);
288
igraph_vector_destroy(&result);
291
PyObject* csize_o = igraphmodule_vector_t_to_PyList(csize_p, IGRAPHMODULE_TYPE_INT);
292
igraph_vector_destroy(csize_p);
293
if (csize_o) return Py_BuildValue("NN", result_o, csize_o);
301
/* Attribute handlers for the Python interface */
304
static int igraphmodule_i_attribute_init(igraph_t *graph, igraph_vector_ptr_t *attr) {
308
attrs=(PyObject**)calloc(3, sizeof(PyObject*));
310
IGRAPH_ERROR("not enough memory to allocate attribute hashes", IGRAPH_ENOMEM);
312
for (i=0; i<3; i++) {
313
attrs[i] = PyDict_New();
314
RC_ALLOC("dict", attrs[i]);
316
graph->attr=(void*)attrs;
318
/* See if we have graph attributes */
320
PyObject *dict=attrs[ATTRHASH_IDX_GRAPH], *value;
322
n = igraph_vector_ptr_size(attr);
323
for (i=0; i<n; i++) {
324
igraph_i_attribute_record_t *attr_rec;
325
attr_rec = VECTOR(*attr)[i];
326
switch (attr_rec->type) {
327
case IGRAPH_ATTRIBUTE_NUMERIC:
328
value=PyFloat_FromDouble((double)VECTOR(*(igraph_vector_t*)attr_rec->value)[0]);
330
case IGRAPH_ATTRIBUTE_STRING:
331
igraph_strvector_get((igraph_strvector_t*)attr_rec->value, 0, &s);
332
if (s == 0) value=PyString_FromString("");
333
else value=PyString_FromString(s);
336
IGRAPH_WARNING("unsupported attribute type (not string and not numeric)");
341
if (PyDict_SetItemString(dict, attr_rec->name, value)) {
346
free(graph->attr); graph->attr = 0;
347
IGRAPH_ERROR("failed to add attributes to graph attribute hash",
356
return IGRAPH_SUCCESS;
360
static void igraphmodule_i_attribute_destroy(igraph_t *graph) {
364
/* printf("Destroying attribute table\n"); */
366
attrs=(PyObject**)graph->attr;
367
for (i=0; i<3; i++) {
375
static int igraphmodule_i_attribute_copy(igraph_t *to, const igraph_t *from,
376
igraph_bool_t ga, igraph_bool_t va, igraph_bool_t ea) {
377
PyObject **fromattrs, **toattrs, *key, *value, *newval, *o=NULL;
378
igraph_bool_t copy_attrs[3] = { ga, va, ea };
383
fromattrs=(PyObject**)from->attr;
384
/* what to do with the original value of toattrs? */
385
toattrs=to->attr=(PyObject**)calloc(3, sizeof(PyObject*));
386
for (i=0; i<3; i++) {
387
if (!copy_attrs[i]) {
388
toattrs[i] = PyDict_New();
389
RC_ALLOC("dict (copying, empty)", toattrs[i]);
393
if (!PyDict_Check(fromattrs[i])) {
394
toattrs[i]=fromattrs[i];
399
toattrs[i]=PyDict_New();
400
RC_ALLOC("dict (copying)", toattrs[i]);
403
while (PyDict_Next(fromattrs[i], &pos, &key, &value)) {
404
/* value is only borrowed, so copy it */
406
newval=PyList_New(PyList_GET_SIZE(value));
407
for (j=0; j<PyList_GET_SIZE(value); j++) {
408
o=PyList_GetItem(value, j);
410
PyList_SetItem(newval, j, o);
416
PyDict_SetItem(toattrs[i], key, newval);
417
Py_DECREF(newval); /* compensate for PyDict_SetItem */
421
return IGRAPH_SUCCESS;
424
/* Adding vertices */
425
static int igraphmodule_i_attribute_add_vertices(igraph_t *graph, long int nv, igraph_vector_ptr_t *attr) {
426
/* Extend the end of every value in the vertex hash with nv pieces of None */
427
PyObject *key, *value, *dict;
429
igraph_i_attribute_record_t *attr_rec;
430
igraph_bool_t *added_attrs=0;
433
if (!graph->attr) return IGRAPH_SUCCESS;
434
if (nv<0) return IGRAPH_SUCCESS;
437
added_attrs = (igraph_bool_t*)calloc((size_t)igraph_vector_ptr_size(attr),
438
sizeof(igraph_bool_t));
440
IGRAPH_ERROR("can't add vertex attributes", IGRAPH_ENOMEM);
441
IGRAPH_FINALLY(free, added_attrs);
444
dict=((PyObject**)graph->attr)[1];
445
if (!PyDict_Check(dict))
446
IGRAPH_ERROR("vertex attribute hash type mismatch", IGRAPH_EINVAL);
448
while (PyDict_Next(dict, &pos, &key, &value)) {
449
if (!PyString_Check(key))
450
IGRAPH_ERROR("vertex attribute hash key is not a string", IGRAPH_EINVAL);
451
if (!PyList_Check(value))
452
IGRAPH_ERROR("vertex attribute hash member is not a list", IGRAPH_EINVAL);
453
/* Check if we have specific values for the given attribute */
456
j=igraph_vector_ptr_size(attr);
457
for (i=0; i<j; i++) {
458
attr_rec=VECTOR(*attr)[i];
459
if (!strcmp(attr_rec->name, PyString_AS_STRING(key))) {
466
/* If we have specific values for the given attribute, attr_rec contains
467
* the appropriate vector. If not, it is null. */
469
for (i=0; i<nv; i++) {
472
switch (attr_rec->type) {
473
case IGRAPH_ATTRIBUTE_NUMERIC:
474
o=PyFloat_FromDouble((double)VECTOR(*(igraph_vector_t*)attr_rec->value)[i]);
476
case IGRAPH_ATTRIBUTE_STRING:
477
igraph_strvector_get((igraph_strvector_t*)attr_rec->value, i, &s);
478
o=PyString_FromString(s);
481
IGRAPH_WARNING("unsupported attribute type (not string and not numeric)");
486
if (PyList_Append(value, o) == -1)
487
IGRAPH_ERROR("can't extend a vertex attribute hash member", IGRAPH_FAILURE);
492
for (i=0; i<nv; i++) {
493
if (PyList_Append(value, Py_None) == -1) {
494
IGRAPH_ERROR("can't extend a vertex attribute hash member", IGRAPH_FAILURE);
500
/* Okay, now we added the new attribute values for the already existing
501
* attribute keys. Let's see if we have something left */
503
l=igraph_vector_ptr_size(attr);
504
j=igraph_vcount(graph)-nv;
505
/* j contains the number of vertices EXCLUDING the new ones! */
506
for (k=0; k<l; k++) {
507
if (added_attrs[k]) continue;
508
attr_rec=(igraph_i_attribute_record_t*)VECTOR(*attr)[k];
510
value=PyList_New(j + nv);
512
IGRAPH_ERROR("can't add attributes", IGRAPH_ENOMEM);
515
for (i=0; i<j; i++) {
517
PyList_SET_ITEM(value, i, Py_None);
520
for (i=0; i<nv; i++) {
523
switch (attr_rec->type) {
524
case IGRAPH_ATTRIBUTE_NUMERIC:
525
o=PyFloat_FromDouble((double)VECTOR(*(igraph_vector_t*)attr_rec->value)[i]);
527
case IGRAPH_ATTRIBUTE_STRING:
528
igraph_strvector_get((igraph_strvector_t*)attr_rec->value, i, &s);
529
o=PyString_FromString(s);
532
IGRAPH_WARNING("unsupported attribute type (not string and not numeric)");
536
if (o) PyList_SET_ITEM(value, i+j, o);
539
PyDict_SetItemString(dict, attr_rec->name, value);
540
Py_DECREF(value); /* compensate for PyDict_SetItemString */
543
IGRAPH_FINALLY_CLEAN(1);
546
return IGRAPH_SUCCESS;
549
static void igraphmodule_i_attribute_delete_edges(igraph_t *graph, const igraph_vector_t *idx);
551
/* Deleting vertices */
552
static void igraphmodule_i_attribute_delete_vertices(igraph_t *graph,
553
const igraph_vector_t *eidx,
554
const igraph_vector_t *vidx) {
555
long int n, i, ndeleted=0;
556
PyObject *key, *value, *dict, *o;
559
/* Reindexing vertices */
560
dict=((PyObject**)graph->attr)[1];
561
if (!PyDict_Check(dict)) return;
563
n=igraph_vector_size(vidx);
564
for (i=0; i<n; i++) {
565
/*printf("%ld:%f ", i, VECTOR(*idx)[i]);*/
566
if (!VECTOR(*vidx)[i]) {
572
/* TODO: maybe it would be more efficient to get the values from the
573
* hash in advance? */
574
while (PyDict_Next(dict, &pos, &key, &value)) {
575
/* Move the element from index i to VECTOR(*idx)[i]-1 */
576
o=PyList_GetItem(value, i);
578
/* IndexError is already set, clear it and return */
582
Py_INCREF(o); /* take ownership, since PyList_SetItem will steal it */
583
PyList_SetItem(value, VECTOR(*vidx)[i]-1, o);
588
/* Clear the remaining parts of the lists that aren't needed anymore */
590
while (PyDict_Next(dict, &pos, &key, &value)) {
591
n=PySequence_Size(value);
592
if (PySequence_DelSlice(value, n-ndeleted, n) == -1) return;
593
/*printf("key: "); PyObject_Print(key, stdout, Py_PRINT_RAW); printf("\n");
594
printf("value: "); PyObject_Print(value, stdout, Py_PRINT_RAW); printf("\n");*/
597
igraphmodule_i_attribute_delete_edges(graph, eidx);
603
static int igraphmodule_i_attribute_add_edges(igraph_t *graph, const igraph_vector_t *edges, igraph_vector_ptr_t *attr) {
604
/* Extend the end of every value in the edge hash with ne pieces of None */
605
PyObject *key, *value, *dict;
607
long int i, j, k, l, ne;
608
igraph_bool_t *added_attrs=0;
609
igraph_i_attribute_record_t *attr_rec;
611
ne=igraph_vector_size(edges)/2;
612
if (!graph->attr) return IGRAPH_SUCCESS;
613
if (ne<0) return IGRAPH_SUCCESS;
616
added_attrs = (igraph_bool_t*)calloc((size_t)igraph_vector_ptr_size(attr),
617
sizeof(igraph_bool_t));
619
IGRAPH_ERROR("can't add vertex attributes", IGRAPH_ENOMEM);
620
IGRAPH_FINALLY(free, added_attrs);
623
dict=((PyObject**)graph->attr)[2];
624
if (!PyDict_Check(dict))
625
IGRAPH_ERROR("edge attribute hash type mismatch", IGRAPH_EINVAL);
626
while (PyDict_Next(dict, &pos, &key, &value)) {
627
if (!PyString_Check(key))
628
IGRAPH_ERROR("edge attribute hash key is not a string", IGRAPH_EINVAL);
629
if (!PyList_Check(value))
630
IGRAPH_ERROR("edge attribute hash member is not a list", IGRAPH_EINVAL);
632
/* Check if we have specific values for the given attribute */
635
j=igraph_vector_ptr_size(attr);
636
for (i=0; i<j; i++) {
637
attr_rec=VECTOR(*attr)[i];
638
if (!strcmp(attr_rec->name, PyString_AS_STRING(key))) {
645
/* If we have specific values for the given attribute, attr_rec contains
646
* the appropriate vector. If not, it is null. */
648
for (i=0; i<ne; i++) {
651
switch (attr_rec->type) {
652
case IGRAPH_ATTRIBUTE_NUMERIC:
653
o=PyFloat_FromDouble((double)VECTOR(*(igraph_vector_t*)attr_rec->value)[i]);
655
case IGRAPH_ATTRIBUTE_STRING:
656
igraph_strvector_get((igraph_strvector_t*)attr_rec->value, i, &s);
657
o=PyString_FromString(s);
660
IGRAPH_WARNING("unsupported attribute type (not string and not numeric)");
665
if (PyList_Append(value, o) == -1)
666
IGRAPH_ERROR("can't extend an edge attribute hash member", IGRAPH_FAILURE);
671
for (i=0; i<ne; i++) {
672
if (PyList_Append(value, Py_None) == -1) {
673
IGRAPH_ERROR("can't extend an edge attribute hash member", IGRAPH_FAILURE);
680
while (PyDict_Next(dict, &pos, &key, &value)) {
681
printf("key: "); PyObject_Print(key, stdout, Py_PRINT_RAW); printf("\n");
682
printf("value: "); PyObject_Print(value, stdout, Py_PRINT_RAW); printf("\n");
685
/* Okay, now we added the new attribute values for the already existing
686
* attribute keys. Let's see if we have something left */
688
l=igraph_vector_ptr_size(attr);
689
j=igraph_ecount(graph)-ne;
690
/* j contains the number of edges EXCLUDING the new ones! */
691
for (k=0; k<l; k++) {
692
if (added_attrs[k]) continue;
693
attr_rec=(igraph_i_attribute_record_t*)VECTOR(*attr)[k];
695
value=PyList_New(j+ne);
697
IGRAPH_ERROR("can't add attributes", IGRAPH_ENOMEM);
700
for (i=0; i<j; i++) {
702
PyList_SET_ITEM(value, i, Py_None);
705
for (i=0; i<ne; i++) {
708
switch (attr_rec->type) {
709
case IGRAPH_ATTRIBUTE_NUMERIC:
710
o=PyFloat_FromDouble((double)VECTOR(*(igraph_vector_t*)attr_rec->value)[i]);
712
case IGRAPH_ATTRIBUTE_STRING:
713
igraph_strvector_get((igraph_strvector_t*)attr_rec->value, i, &s);
714
o=PyString_FromString(s);
717
IGRAPH_WARNING("unsupported attribute type (not string and not numeric)");
721
if (o) PyList_SET_ITEM(value, i+j, o);
724
PyDict_SetItemString(dict, attr_rec->name, value);
725
Py_DECREF(value); /* compensate for PyDict_SetItemString */
728
IGRAPH_FINALLY_CLEAN(1);
731
return IGRAPH_SUCCESS;
735
static void igraphmodule_i_attribute_delete_edges(igraph_t *graph, const igraph_vector_t *idx) {
736
long int n, i, ndeleted=0;
737
PyObject *key, *value, *dict, *o;
740
dict=((PyObject**)graph->attr)[2];
741
if (!PyDict_Check(dict)) return;
743
n=igraph_vector_size(idx);
744
for (i=0; i<n; i++) {
745
/* printf("%ld:%f ", i, VECTOR(*idx)[i]); */
746
if (!VECTOR(*idx)[i]) {
752
/* TODO: maybe it would be more efficient to get the values from the
753
* hash in advance? */
754
while (PyDict_Next(dict, &pos, &key, &value)) {
755
/* Move the element from index i to VECTOR(*idx)[i]-1 */
756
o=PyList_GetItem(value, i);
758
/* IndexError is already set, clear it and return */
763
PyList_SetItem(value, VECTOR(*idx)[i]-1, o);
768
/* Clear the remaining parts of the lists that aren't needed anymore */
770
while (PyDict_Next(dict, &pos, &key, &value)) {
771
n=PySequence_Size(value);
772
if (PySequence_DelSlice(value, n-ndeleted, n) == -1) return;
773
/*printf("key: "); PyObject_Print(key, stdout, Py_PRINT_RAW); printf("\n");
774
printf("value: "); PyObject_Print(value, stdout, Py_PRINT_RAW); printf("\n");*/
780
/* Permuting edges */
781
static int igraphmodule_i_attribute_permute_edges(igraph_t *graph,
782
const igraph_vector_t *idx) {
784
PyObject *key, *value, *dict, *newdict, *newlist, *o;
787
dict=((PyObject**)graph->attr)[2];
788
if (!PyDict_Check(dict)) return 1;
790
newdict=PyDict_New();
791
if (!newdict) return 1;
793
n=igraph_vector_size(idx);
796
while (PyDict_Next(dict, &pos, &key, &value)) {
797
newlist=PyList_New(n);
798
for (i=0; i<n; i++) {
799
o=PyList_GetItem(value, VECTOR(*idx)[i]-1);
805
PyList_SET_ITEM(newlist, i, o);
807
PyDict_SetItem(newdict, key, newlist);
811
((PyObject**)graph->attr)[2]=newdict;
817
/* Getting attribute names and types */
818
static int igraphmodule_i_attribute_get_info(const igraph_t *graph,
819
igraph_strvector_t *gnames,
820
igraph_vector_t *gtypes,
821
igraph_strvector_t *vnames,
822
igraph_vector_t *vtypes,
823
igraph_strvector_t *enames,
824
igraph_vector_t *etypes) {
825
igraph_strvector_t *names[3] = { gnames, vnames, enames };
826
igraph_vector_t *types[3] = { gtypes, vtypes, etypes };
827
long int i, j, k, l, m;
829
for (i=0; i<3; i++) {
830
igraph_strvector_t *n = names[i];
831
igraph_vector_t *t = types[i];
832
PyObject *dict = ((PyObject**)graph->attr)[i];
836
keys=PyDict_Keys(dict);
837
if (!keys) IGRAPH_ERROR("Internal error in PyDict_Keys", IGRAPH_FAILURE);
840
j=igraphmodule_PyList_to_strvector_t(keys, n);
845
igraph_vector_init(t, k);
846
for (j=0; j<k; j++) {
848
values=PyDict_GetItem(dict, PyList_GetItem(keys, j));
849
if (PyList_Check(values)) {
850
m=PyList_Size(values);
851
for (l=0; l<m && is_numeric; l++) {
852
o=PyList_GetItem(values, l);
853
if (o != Py_None && !PyNumber_Check(o)) is_numeric=0;
855
} else if (o != Py_None && !PyNumber_Check(values)) is_numeric=0;
857
VECTOR(*t)[j]=is_numeric ? IGRAPH_ATTRIBUTE_NUMERIC : IGRAPH_ATTRIBUTE_STRING;
867
/* Checks whether the graph has a graph/vertex/edge attribute with the given name */
868
igraph_bool_t igraphmodule_i_attribute_has_attr(const igraph_t *graph,
869
igraph_attribute_elemtype_t type,
874
case IGRAPH_ATTRIBUTE_GRAPH: attrnum=0; break;
875
case IGRAPH_ATTRIBUTE_VERTEX: attrnum=1; break;
876
case IGRAPH_ATTRIBUTE_EDGE: attrnum=2; break;
877
default: return 0; break;
879
dict = ((PyObject**)graph->attr)[attrnum];
880
o = PyDict_GetItemString(dict, name);
884
/* Returns the type of a given attribute */
885
int igraphmodule_i_attribute_get_type(const igraph_t *graph,
886
igraph_attribute_type_t *type,
887
igraph_attribute_elemtype_t elemtype,
889
long int attrnum, i, j;
890
int is_numeric, is_string;
894
case IGRAPH_ATTRIBUTE_GRAPH: attrnum=ATTRHASH_IDX_GRAPH; break;
895
case IGRAPH_ATTRIBUTE_VERTEX: attrnum=ATTRHASH_IDX_VERTEX; break;
896
case IGRAPH_ATTRIBUTE_EDGE: attrnum=ATTRHASH_IDX_EDGE; break;
897
default: IGRAPH_ERROR("No such attribute type", IGRAPH_EINVAL); break;
900
/* Get the attribute dict */
901
dict = ((PyObject**)graph->attr)[attrnum];
903
/* Check whether the attribute exists */
904
o = PyDict_GetItemString(dict, name);
905
if (o == 0) IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL);
907
/* Basic type check */
908
if (!PyList_Check(o)) IGRAPH_ERROR("attribute hash type mismatch", IGRAPH_EINVAL);
911
*type = IGRAPH_ATTRIBUTE_NUMERIC;
915
/* Go on with the checks */
916
is_numeric = is_string = 1;
919
for (i=0; i<j && is_numeric; i++) {
920
PyObject *item = PyList_GET_ITEM(o, i);
921
if (item != Py_None && !PyNumber_Check(item)) is_numeric=0;
923
for (i=0; i<j && is_string; i++) {
924
PyObject *item = PyList_GET_ITEM(o, i);
925
if (item != Py_None && !PyString_Check(item) && !PyUnicode_Check(item))
929
if (o != Py_None && !PyNumber_Check(o)) is_numeric=0;
930
if (o != Py_None && !PyString_Check(o) && !PyUnicode_Check(o))
934
*type = IGRAPH_ATTRIBUTE_NUMERIC;
936
*type = IGRAPH_ATTRIBUTE_STRING;
938
*type = IGRAPH_ATTRIBUTE_PY_OBJECT;
942
/* Getting numeric graph attributes */
943
int igraphmodule_i_get_numeric_graph_attr(const igraph_t *graph,
944
const char *name, igraph_vector_t *value) {
945
PyObject *dict, *o, *result;
946
dict = ((PyObject**)graph->attr)[0];
947
/* No error checking, if we get here, the type has already been checked by previous
948
attribute handler calls... hopefully :) Same applies for the other handlers. */
949
o = PyDict_GetItemString(dict, name);
950
if (!o) IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL);
951
IGRAPH_CHECK(igraph_vector_resize(value, 1));
953
VECTOR(*value)[0] = IGRAPH_NAN;
956
result = PyNumber_Float(o);
958
VECTOR(*value)[0] = PyFloat_AsDouble(o);
960
} else IGRAPH_ERROR("Internal error in PyFloat_AsDouble", IGRAPH_EINVAL);
965
/* Getting string graph attributes */
966
int igraphmodule_i_get_string_graph_attr(const igraph_t *graph,
967
const char *name, igraph_strvector_t *value) {
968
PyObject *dict, *o, *result;
969
dict = ((PyObject**)graph->attr)[0];
970
o = PyDict_GetItemString(dict, name);
971
if (!o) IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL);
972
IGRAPH_CHECK(igraph_strvector_resize(value, 1));
973
if (PyUnicode_Check(o)) {
974
result = PyUnicode_AsEncodedString(o, "utf-8", "xmlcharrefreplace");
976
result = PyObject_Str(o);
979
IGRAPH_CHECK(igraph_strvector_set(value, 0, PyString_AsString(result)));
981
} else IGRAPH_ERROR("Internal error in PyObject_Str", IGRAPH_EINVAL);
986
/* Getting numeric vertex attributes */
987
int igraphmodule_i_get_numeric_vertex_attr(const igraph_t *graph,
990
igraph_vector_t *value) {
991
PyObject *dict, *list, *result, *o;
992
igraph_vector_t newvalue;
994
dict = ((PyObject**)graph->attr)[1];
995
list = PyDict_GetItemString(dict, name);
996
if (!list) IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL);
998
if (igraph_vs_is_all(&vs)) {
999
if (igraphmodule_PyObject_float_to_vector_t(list, &newvalue))
1000
IGRAPH_ERROR("Internal error", IGRAPH_EINVAL);
1001
igraph_vector_update(value, &newvalue);
1002
igraph_vector_destroy(&newvalue);
1006
IGRAPH_CHECK(igraph_vit_create(graph, vs, &it));
1007
IGRAPH_FINALLY(igraph_vit_destroy, &it);
1008
IGRAPH_CHECK(igraph_vector_resize(value, IGRAPH_VIT_SIZE(it)));
1009
while (!IGRAPH_VIT_END(it)) {
1010
long int v=IGRAPH_VIT_GET(it);
1011
o = PyList_GetItem(list, v);
1013
result = PyNumber_Float(o);
1014
VECTOR(*value)[i] = PyFloat_AsDouble(result);
1016
} else VECTOR(*value)[i] = IGRAPH_NAN;
1017
IGRAPH_VIT_NEXT(it);
1020
igraph_vit_destroy(&it);
1021
IGRAPH_FINALLY_CLEAN(1);
1027
/* Getting string vertex attributes */
1028
int igraphmodule_i_get_string_vertex_attr(const igraph_t *graph,
1031
igraph_strvector_t *value) {
1032
PyObject *dict, *list, *result;
1033
igraph_strvector_t newvalue;
1035
dict = ((PyObject**)graph->attr)[1];
1036
list = PyDict_GetItemString(dict, name);
1037
if (!list) IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL);
1039
if (igraph_vs_is_all(&vs)) {
1040
if (igraphmodule_PyList_to_strvector_t(list, &newvalue))
1041
IGRAPH_ERROR("Internal error", IGRAPH_EINVAL);
1042
igraph_strvector_destroy(value);
1047
IGRAPH_CHECK(igraph_vit_create(graph, vs, &it));
1048
IGRAPH_FINALLY(igraph_vit_destroy, &it);
1049
IGRAPH_CHECK(igraph_strvector_resize(value, IGRAPH_VIT_SIZE(it)));
1050
while (!IGRAPH_VIT_END(it)) {
1051
long int v=IGRAPH_VIT_GET(it);
1052
result = PyList_GetItem(list, v);
1053
if (PyUnicode_Check(result)) {
1054
result = PyUnicode_AsEncodedString(result, "utf-8", "xmlcharrefreplace");
1056
result = PyObject_Str(result);
1059
IGRAPH_ERROR("Internal error in PyObject_Str", IGRAPH_EINVAL);
1061
igraph_strvector_set(value, i, PyString_AsString(result));
1063
IGRAPH_VIT_NEXT(it);
1066
igraph_vit_destroy(&it);
1067
IGRAPH_FINALLY_CLEAN(1);
1073
/* Getting numeric edge attributes */
1074
int igraphmodule_i_get_numeric_edge_attr(const igraph_t *graph,
1077
igraph_vector_t *value) {
1078
PyObject *dict, *list, *result, *o;
1079
igraph_vector_t newvalue;
1081
dict = ((PyObject**)graph->attr)[2];
1082
list = PyDict_GetItemString(dict, name);
1083
if (!list) IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL);
1085
if (igraph_es_is_all(&es)) {
1086
if (igraphmodule_PyObject_float_to_vector_t(list, &newvalue))
1087
IGRAPH_ERROR("Internal error", IGRAPH_EINVAL);
1088
igraph_vector_update(value, &newvalue);
1089
igraph_vector_destroy(&newvalue);
1093
IGRAPH_CHECK(igraph_eit_create(graph, es, &it));
1094
IGRAPH_FINALLY(igraph_eit_destroy, &it);
1095
IGRAPH_CHECK(igraph_vector_resize(value, IGRAPH_EIT_SIZE(it)));
1096
while (!IGRAPH_EIT_END(it)) {
1097
long int v=IGRAPH_EIT_GET(it);
1098
o = PyList_GetItem(list, v);
1100
result = PyNumber_Float(o);
1101
VECTOR(*value)[i] = PyFloat_AsDouble(result);
1103
} else VECTOR(*value)[i] = IGRAPH_NAN;
1104
IGRAPH_EIT_NEXT(it);
1107
igraph_eit_destroy(&it);
1108
IGRAPH_FINALLY_CLEAN(1);
1114
/* Getting string edge attributes */
1115
int igraphmodule_i_get_string_edge_attr(const igraph_t *graph,
1118
igraph_strvector_t *value) {
1119
PyObject *dict, *list, *result;
1120
igraph_strvector_t newvalue;
1122
dict = ((PyObject**)graph->attr)[2];
1123
list = PyDict_GetItemString(dict, name);
1124
if (!list) IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL);
1126
if (igraph_es_is_all(&es)) {
1127
if (igraphmodule_PyList_to_strvector_t(list, &newvalue))
1128
IGRAPH_ERROR("Internal error", IGRAPH_EINVAL);
1129
igraph_strvector_destroy(value);
1134
IGRAPH_CHECK(igraph_eit_create(graph, es, &it));
1135
IGRAPH_FINALLY(igraph_eit_destroy, &it);
1136
IGRAPH_CHECK(igraph_strvector_resize(value, IGRAPH_EIT_SIZE(it)));
1137
while (!IGRAPH_EIT_END(it)) {
1138
long int v=IGRAPH_EIT_GET(it);
1139
result = PyList_GetItem(list, v);
1140
if (PyUnicode_Check(result)) {
1141
result = PyUnicode_AsEncodedString(result, "utf-8", "xmlcharrefreplace");
1143
result = PyObject_Str(result);
1146
IGRAPH_ERROR("Internal error in PyObject_Str", IGRAPH_EINVAL);
1148
igraph_strvector_set(value, i, PyString_AsString(result));
1150
IGRAPH_EIT_NEXT(it);
1153
igraph_eit_destroy(&it);
1154
IGRAPH_FINALLY_CLEAN(1);
1160
static igraph_attribute_table_t igraphmodule_i_attribute_table = {
1161
igraphmodule_i_attribute_init,
1162
igraphmodule_i_attribute_destroy,
1163
igraphmodule_i_attribute_copy,
1164
igraphmodule_i_attribute_add_vertices,
1165
igraphmodule_i_attribute_delete_vertices,
1166
igraphmodule_i_attribute_add_edges,
1167
igraphmodule_i_attribute_delete_edges,
1168
igraphmodule_i_attribute_permute_edges,
1169
igraphmodule_i_attribute_get_info,
1170
igraphmodule_i_attribute_has_attr,
1171
igraphmodule_i_attribute_get_type,
1172
igraphmodule_i_get_numeric_graph_attr,
1173
igraphmodule_i_get_string_graph_attr,
1174
igraphmodule_i_get_numeric_vertex_attr,
1175
igraphmodule_i_get_string_vertex_attr,
1176
igraphmodule_i_get_numeric_edge_attr,
1177
igraphmodule_i_get_string_edge_attr,
1180
/** \ingroup python_interface
1181
* \brief Method table for the igraph Python module
1183
static PyMethodDef igraphmodule_methods[] =
1185
{"community_to_membership", (PyCFunction)igraphmodule_community_to_membership,
1186
METH_VARARGS | METH_KEYWORDS,
1187
"community_to_membership(merges, nodes, steps, return_csize=False)\n\n"
1189
{"convex_hull", (PyCFunction)igraphmodule_convex_hull, METH_VARARGS,
1190
"convex_hull(vs, coords=False)\n\n"
1191
"Calculates the convex hull of a given point set.\n\n"
1192
"@param vs: the point set as a list of lists\n"
1193
"@param coords: if C{True}, the function returns the\n"
1194
" coordinates of the corners of the convex hull polygon,\n"
1195
" otherwise returns the corner indices.\n"
1196
"@return: either the hull's corner coordinates or the point\n"
1197
" indices corresponding to them, depending on the C{coords}\n"
1200
{"set_progress_handler", igraphmodule_set_progress_handler, METH_VARARGS,
1201
"set_progress_handler(handler)\n\n"
1202
"Sets the handler to be called when igraph is performing a long operation.\n"
1203
"@param handler: the progress handler function. It must accept two\n"
1204
" arguments, the first is the message informing the user about\n"
1205
" what igraph is doing right now, the second is the actual\n"
1206
" progress information (a percentage).\n"
1208
{NULL, NULL, 0, NULL}
1211
#ifndef PyMODINIT_FUNC
1212
#define PyMODINIT_FUNC void
1215
extern PyObject* igraphmodule_InternalError;
1216
extern PyObject* igraphmodule_arpack_options_default;
1218
PyMODINIT_FUNC initcore(void) {
1221
if (PyType_Ready(&igraphmodule_VertexSeqType) < 0) return;
1222
if (PyType_Ready(&igraphmodule_EdgeSeqType) < 0) return;
1224
igraphmodule_VertexType.tp_clear = (inquiry)igraphmodule_Vertex_clear;
1225
if (PyType_Ready(&igraphmodule_VertexType) < 0) return;
1227
igraphmodule_EdgeType.tp_clear = (inquiry)igraphmodule_Edge_clear;
1228
if (PyType_Ready(&igraphmodule_EdgeType) < 0) return;
1230
if (PyType_Ready(&igraphmodule_GraphType) < 0) return;
1231
if (PyType_Ready(&igraphmodule_BFSIterType) < 0) return;
1232
if (PyType_Ready(&igraphmodule_ARPACKOptionsType) < 0) return;
1234
m = Py_InitModule3("igraph.core", igraphmodule_methods,
1235
"Low-level Python interface for the igraph library. "
1236
"Should not be used directly.");
1238
PyModule_AddObject(m, "GraphBase", (PyObject*)&igraphmodule_GraphType);
1239
PyModule_AddObject(m, "BFSIter", (PyObject*)&igraphmodule_BFSIterType);
1240
PyModule_AddObject(m, "ARPACKOptions", (PyObject*)&igraphmodule_ARPACKOptionsType);
1241
PyModule_AddObject(m, "Edge", (PyObject*)&igraphmodule_EdgeType);
1242
PyModule_AddObject(m, "EdgeSeq", (PyObject*)&igraphmodule_EdgeSeqType);
1243
PyModule_AddObject(m, "Vertex", (PyObject*)&igraphmodule_VertexType);
1244
PyModule_AddObject(m, "VertexSeq", (PyObject*)&igraphmodule_VertexSeqType);
1246
/* Internal error exception type */
1247
igraphmodule_InternalError =
1248
PyErr_NewException("igraph.core.InternalError", PyExc_Exception, NULL);
1249
PyModule_AddObject(m, "InternalError", igraphmodule_InternalError);
1251
/* ARPACK default options variable */
1252
igraphmodule_arpack_options_default = igraphmodule_ARPACKOptions_new();
1253
PyModule_AddObject(m, "arpack_options", igraphmodule_arpack_options_default);
1255
/* Useful constants */
1256
PyModule_AddIntConstant(m, "OUT", IGRAPH_OUT);
1257
PyModule_AddIntConstant(m, "IN", IGRAPH_IN);
1258
PyModule_AddIntConstant(m, "ALL", IGRAPH_ALL);
1260
PyModule_AddIntConstant(m, "STAR_OUT", IGRAPH_STAR_OUT);
1261
PyModule_AddIntConstant(m, "STAR_IN", IGRAPH_STAR_IN);
1262
PyModule_AddIntConstant(m, "STAR_UNDIRECTED", IGRAPH_STAR_UNDIRECTED);
1264
PyModule_AddIntConstant(m, "TREE_OUT", IGRAPH_TREE_OUT);
1265
PyModule_AddIntConstant(m, "TREE_IN", IGRAPH_TREE_IN);
1266
PyModule_AddIntConstant(m, "TREE_UNDIRECTED", IGRAPH_TREE_UNDIRECTED);
1268
PyModule_AddIntConstant(m, "STRONG", IGRAPH_STRONG);
1269
PyModule_AddIntConstant(m, "WEAK", IGRAPH_WEAK);
1271
PyModule_AddIntConstant(m, "GET_ADJACENCY_UPPER", IGRAPH_GET_ADJACENCY_UPPER);
1272
PyModule_AddIntConstant(m, "GET_ADJACENCY_LOWER", IGRAPH_GET_ADJACENCY_LOWER);
1273
PyModule_AddIntConstant(m, "GET_ADJACENCY_BOTH", IGRAPH_GET_ADJACENCY_BOTH);
1275
PyModule_AddIntConstant(m, "REWIRING_SIMPLE", IGRAPH_REWIRING_SIMPLE);
1277
PyModule_AddIntConstant(m, "ADJ_DIRECTED", IGRAPH_ADJ_DIRECTED);
1278
PyModule_AddIntConstant(m, "ADJ_UNDIRECTED", IGRAPH_ADJ_UNDIRECTED);
1279
PyModule_AddIntConstant(m, "ADJ_MAX", IGRAPH_ADJ_MAX);
1280
PyModule_AddIntConstant(m, "ADJ_MIN", IGRAPH_ADJ_MIN);
1281
PyModule_AddIntConstant(m, "ADJ_PLUS", IGRAPH_ADJ_PLUS);
1282
PyModule_AddIntConstant(m, "ADJ_UPPER", IGRAPH_ADJ_UPPER);
1283
PyModule_AddIntConstant(m, "ADJ_LOWER", IGRAPH_ADJ_LOWER);
1285
PyModule_AddIntConstant(m, "BLISS_F", IGRAPH_BLISS_F);
1286
PyModule_AddIntConstant(m, "BLISS_FL", IGRAPH_BLISS_FL);
1287
PyModule_AddIntConstant(m, "BLISS_FS", IGRAPH_BLISS_FS);
1288
PyModule_AddIntConstant(m, "BLISS_FM", IGRAPH_BLISS_FM);
1289
PyModule_AddIntConstant(m, "BLISS_FLM", IGRAPH_BLISS_FLM);
1290
PyModule_AddIntConstant(m, "BLISS_FSM", IGRAPH_BLISS_FSM);
1292
/* More useful constants */
1293
PyModule_AddStringConstant(m, "__version__", "0.5.4");
1294
PyModule_AddStringConstant(m, "__build_date__", __DATE__);
1296
/* initialize error, progress, warning and interruption handler */
1297
igraph_set_error_handler(igraphmodule_igraph_error_hook);
1298
igraph_set_progress_handler(igraphmodule_igraph_progress_hook);
1299
igraph_set_warning_handler(igraphmodule_igraph_warning_hook);
1300
igraph_set_interruption_handler(igraphmodule_igraph_interrupt_hook);
1302
/* initialize attribute handlers */
1303
igraph_i_set_attribute_table(&igraphmodule_i_attribute_table);