/* -*- mode: C -*- */
/*
IGraph library.
Copyright (C) 2006 Gabor Csardi <csardi@rmki.kfki.hu>
MTA RMKI, Konkoly-Thege Miklos st. 29-33, Budapest 1121, Hungary
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include <Python.h>
#include <pythonrun.h>
#include "igraph.h"
#include "memory.h"
#include "attributes.h"
#include "common.h"
#include "error.h"
#include "convert.h"
#include "graphobject.h"
#include "vertexseqobject.h"
#include "vertexobject.h"
#include "edgeseqobject.h"
#include "edgeobject.h"
#include "bfsiter.h"
#include "config.h"
/**
* \defgroup python_interface Python module implementation
* \brief Functions implementing a Python interface to \a igraph
*
* These functions provide a way to access \a igraph functions from Python.
* It should be of interest of \a igraph developers only. Classes, functions
* and methods exposed to Python are still to be documented. Until it is done,
* just type the following to get help about \a igraph functions in Python
* (assuming you have \c igraph.so somewhere in your Python library path):
*
* \verbatim
import igraph
help(igraph)
help(igraph.Graph)
\endverbatim
*
* Most of the functions provided here share the same calling conventions
* (which are determined by the Python/C API). Since the role of the
* arguments are the same across many functions, I won't explain them
* everywhere, just give a quick overview of the common argument names here.
*
* \param self the Python igraph.Graph object the method is working on
* \param args pointer to the Python tuple containing the arguments
* \param kwds pointer to the Python hash containing the keyword parameters
* \param type the type object of a Python igraph.Graph object. Used usually
* in constructors and class methods.
*
* Any arguments not documented here should be mentioned at the documentation
* of the appropriate method.
*
* The functions which implement a Python method always return a pointer to
* a \c PyObject. According to Python conventions, this is \c NULL if and
* only if an exception was thrown by the method (or any of the functions
* it has called). When I explain the return value of a function which
* provides interface to an \a igraph function, I won't cover the case of
* returning a \c NULL value, because this is the same for every such method.
* The conclusion is that a method can return \c NULL even if I don't state
* it explicitly.
*
* Also please take into consideration that I'm documenting the C calls
* with the abovementioned parameters here, and \em not the Python methods
* which are presented to the user using the Python interface of \a igraph.
* If you are looking for the documentation of the classes, methods and
* functions exposed to Python, please use the \c help calls from Python
* or use \c pydoc to generate a formatted version.
*
* \section weakrefs The usage of weak references in the Python interface
*
* Many classes implemented in the Python interface (e.g. VertexSeq, Vertex...)
* use weak references to keep track of the graph they are referencing to.
* The use of weak references is twofold:
*
* -# If we assign a VertexSeq or a Vertex of a given graph to a local
* variable and then destroy the graph, real references keep the graph
* alive and do not return the memory back to Python.
* -# If we use real references, a Graph object will hold a reference
* to its VertexSeq (because we don't want to allocate a new VertexSeq
* object for the same graph every time it is requested), and the
* VertexSeq will also hold a reference to the Graph. This is a circular
* reference. Python does not reclaim the memory occupied by the Graph
* back when the Graph is destroyed, because the VertexSeq is holding a
* reference to it. Similarly, VertexSeq doesn't get freed because the
* Graph is holding a reference to it. These situations can only be
* resolved by the Python garbage collector which is invoked at regular
* intervals. Unfortunately, the garbage collector refuses to break
* circular references and free the objects participating in the circle
* when any of the objects has a \c __del__ method. In this case,
* \c igraph.Graph has one (which frees the underlying \c igraph_t
* graph), therefore our graphs never get freed when we use real
* references.
*/
static PyObject* igraphmodule_progress_handler=NULL;
static int igraphmodule_igraph_interrupt_hook(void* data) {
if (PyErr_CheckSignals()) {
return IGRAPH_FAILURE;
}
return IGRAPH_SUCCESS;
}
int igraphmodule_igraph_progress_hook(const char* message, igraph_real_t percent,
void* data) {
if (igraphmodule_progress_handler) {
PyObject *result;
if (PyCallable_Check(igraphmodule_progress_handler)) {
result=PyObject_CallFunction(igraphmodule_progress_handler,
"sd", message, (double)percent);
Py_DECREF(result);
}
}
return 0;
}
PyObject* igraphmodule_set_progress_handler(PyObject* self, PyObject* args) {
PyObject* o;
if (!PyArg_ParseTuple(args, "O", &o)) return NULL;
if (!PyCallable_Check(o)) {
PyErr_SetString(PyExc_TypeError, "Progress handler must be callable.");
return NULL;
}
igraphmodule_progress_handler=o;
Py_RETURN_NONE;
}
PyObject* igraphmodule_convex_hull(PyObject* self, PyObject* args, PyObject* kwds) {
char* kwlist[] = {"vs", "coords", NULL};
PyObject *vs, *o, *o1=0, *o2=0, *coords = Py_False;
igraph_matrix_t mtrx;
igraph_vector_t result;
igraph_matrix_t resmat;
long no_of_nodes, i;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|O", kwlist, &PyList_Type, &vs, &coords))
return NULL;
no_of_nodes=PyList_Size(vs);
if (igraph_matrix_init(&mtrx, no_of_nodes, 2)) {
igraphmodule_handle_igraph_error();
return NULL;
}
for (i=0; i<no_of_nodes; i++) {
o=PyList_GetItem(vs, i);
if (PyList_Check(o)) {
if (PyList_Size(o) >= 2) {
o1=PyList_GetItem(o, 0);
o2=PyList_GetItem(o, 1);
if (PyList_Size(o) > 2)
PyErr_Warn(PyExc_Warning, "vertex with more than 2 coordinates found, considering only the first 2");
} else {
PyErr_SetString(PyExc_TypeError, "vertex with less than 2 coordinates found");
igraph_matrix_destroy(&mtrx);
return NULL;
}
} else if (PyTuple_Check(o)) {
if (PyTuple_Size(o) >= 2) {
o1=PyTuple_GetItem(o, 0);
o2=PyTuple_GetItem(o, 1);
if (PyTuple_Size(o) > 2)
PyErr_Warn(PyExc_Warning, "vertex with more than 2 coordinates found, considering only the first 2");
} else {
PyErr_SetString(PyExc_TypeError, "vertex with less than 2 coordinates found");
igraph_matrix_destroy(&mtrx);
return NULL;
}
}
if (!PyNumber_Check(o1) || !PyNumber_Check(o2)) {
PyErr_SetString(PyExc_TypeError, "vertex coordinates must be numeric");
igraph_matrix_destroy(&mtrx);
return NULL;
}
/* o, o1 and o2 were borrowed, but now o1 and o2 are actual references! */
o1=PyNumber_Float(o1); o2=PyNumber_Float(o2);
if (!o1 || !o2) {
PyErr_SetString(PyExc_TypeError, "vertex coordinate conversion to float failed");
Py_XDECREF(o1);
Py_XDECREF(o2);
igraph_matrix_destroy(&mtrx);
return NULL;
}
MATRIX(mtrx, i, 0)=(igraph_real_t)PyFloat_AsDouble(o1);
MATRIX(mtrx, i, 1)=(igraph_real_t)PyFloat_AsDouble(o2);
Py_DECREF(o1);
Py_DECREF(o2);
}
if (!PyObject_IsTrue(coords)) {
if (igraph_vector_init(&result, 0)) {
igraphmodule_handle_igraph_error();
igraph_matrix_destroy(&mtrx);
return NULL;
}
if (igraph_convex_hull(&mtrx, &result, 0)) {
igraphmodule_handle_igraph_error();
igraph_matrix_destroy(&mtrx);
igraph_vector_destroy(&result);
return NULL;
}
o=igraphmodule_vector_t_to_PyList(&result, IGRAPHMODULE_TYPE_INT);
igraph_vector_destroy(&result);
} else {
if (igraph_matrix_init(&resmat, 0, 0)) {
igraphmodule_handle_igraph_error();
igraph_matrix_destroy(&mtrx);
return NULL;
}
if (igraph_convex_hull(&mtrx, 0, &resmat)) {
igraphmodule_handle_igraph_error();
igraph_matrix_destroy(&mtrx);
igraph_matrix_destroy(&resmat);
return NULL;
}
o=igraphmodule_matrix_t_to_PyList(&resmat, IGRAPHMODULE_TYPE_FLOAT);
igraph_matrix_destroy(&resmat);
}
igraph_matrix_destroy(&mtrx);
return o;
}
/* Attribute handlers for the Python interface */
/* Initialization */
static int igraphmodule_i_attribute_init(igraph_t *graph, igraph_vector_ptr_t *attr) {
PyObject** attrs;
long int i, n;
attrs=(PyObject**)calloc(3, sizeof(PyObject*));
/* printf("Created attribute table at %p\n", attrs); */
if (!attrs)
IGRAPH_ERROR("not enough memory to allocate attribute hashes", IGRAPH_ENOMEM);
for (i=0; i<3; i++) {
attrs[i] = PyDict_New();
}
graph->attr=(void*)attrs;
/* See if we have graph attributes */
if (attr) {
PyObject *dict=attrs[ATTRHASH_IDX_GRAPH], *value;
char *s;
n = igraph_vector_ptr_size(attr);
for (i=0; i<n; i++) {
igraph_i_attribute_record_t *attr_rec;
attr_rec = VECTOR(*attr)[i];
switch (attr_rec->type) {
case IGRAPH_ATTRIBUTE_NUMERIC:
value=PyFloat_FromDouble((double)VECTOR(*(igraph_vector_t*)attr_rec->value)[0]);
break;
case IGRAPH_ATTRIBUTE_STRING:
igraph_strvector_get((igraph_strvector_t*)attr_rec->value, 0, &s);
value=PyString_FromString(s);
break;
default:
IGRAPH_WARNING("unsupported attribute type (not string and not numeric)");
value=0;
break;
}
if (value) {
if (PyDict_SetItemString(dict, attr_rec->name, value)) {
Py_DECREF(value);
Py_DECREF(attrs[0]);
Py_DECREF(attrs[1]);
Py_DECREF(attrs[2]);
Free(graph->attr);
IGRAPH_ERROR("failed to add attributes to graph attribute hash",
IGRAPH_FAILURE);
}
Py_DECREF(value);
value=0;
}
}
}
return IGRAPH_SUCCESS;
}
/* Destruction */
static void igraphmodule_i_attribute_destroy(igraph_t *graph) {
PyObject** attrs;
int i;
/* printf("Destroying attribute table\n"); */
if (graph->attr) {
attrs=(PyObject**)graph->attr;
for (i=0; i<3; i++) {
Py_DECREF(attrs[i]);
}
free(attrs);
}
}
/* Copying */
static int igraphmodule_i_attribute_copy(igraph_t *to, const igraph_t *from) {
PyObject **fromattrs, **toattrs, *key, *value, *newval, *o=NULL;
int i, j, pos;
/* printf("Copying attribute table\n"); */
if (from->attr) {
fromattrs=(PyObject**)from->attr;
/* what to do with the original value of toattrs? */
toattrs=to->attr=(PyObject**)calloc(3, sizeof(PyObject*));
for (i=0; i<3; i++) {
if (!PyDict_Check(fromattrs[i])) {
toattrs[i]=fromattrs[i];
Py_XINCREF(o);
continue;
}
toattrs[i]=PyDict_New();
pos=0;
while (PyDict_Next(fromattrs[i], &pos, &key, &value)) {
/* value is only borrowed, so copy it */
if (i>0) {
newval=PyList_New(PyList_GET_SIZE(value));
for (j=0; j<PyList_GET_SIZE(value); j++) {
o=PyList_GetItem(value, j);
Py_INCREF(o);
PyList_SetItem(newval, j, o);
}
} else {
newval=value;
Py_INCREF(newval);
}
PyDict_SetItem(toattrs[i], key, newval);
}
}
}
return IGRAPH_SUCCESS;
}
/* Adding vertices */
static int igraphmodule_i_attribute_add_vertices(igraph_t *graph, long int nv, igraph_vector_ptr_t *attr) {
/* Extend the end of every value in the vertex hash with nv pieces of None */
PyObject *key, *value, *dict;
int pos=0;
long int i, j, k, l;
igraph_i_attribute_record_t *attr_rec;
igraph_bool_t *added_attrs=0;
if (!graph->attr) return IGRAPH_SUCCESS;
if (nv<=0) return IGRAPH_SUCCESS;
if (attr) {
added_attrs = (igraph_bool_t*)calloc((size_t)igraph_vector_ptr_size(attr),
sizeof(igraph_bool_t));
if (!added_attrs)
IGRAPH_ERROR("can't add vertex attributes", IGRAPH_ENOMEM);
IGRAPH_FINALLY(free, added_attrs);
}
dict=((PyObject**)graph->attr)[1];
if (!PyDict_Check(dict))
IGRAPH_ERROR("vertex attribute hash type mismatch", IGRAPH_EINVAL);
while (PyDict_Next(dict, &pos, &key, &value)) {
if (!PyString_Check(key))
IGRAPH_ERROR("vertex attribute hash key is not a string", IGRAPH_EINVAL);
if (!PyList_Check(value))
IGRAPH_ERROR("vertex attribute hash member is not a list", IGRAPH_EINVAL);
/* Check if we have specific values for the given attribute */
attr_rec=0;
if (attr) {
j=igraph_vector_ptr_size(attr);
for (i=0; i<j; i++) {
attr_rec=VECTOR(*attr)[i];
if (!strcmp(attr_rec->name, PyString_AS_STRING(key))) {
added_attrs[i]=1;
break;
}
attr_rec=0;
}
}
/* If we have specific values for the given attribute, attr_rec contains
* the appropriate vector. If not, it is null. */
if (attr_rec) {
for (i=0; i<nv; i++) {
char *s;
PyObject *o;
switch (attr_rec->type) {
case IGRAPH_ATTRIBUTE_NUMERIC:
o=PyFloat_FromDouble((double)VECTOR(*(igraph_vector_t*)attr_rec->value)[i]);
break;
case IGRAPH_ATTRIBUTE_STRING:
igraph_strvector_get((igraph_strvector_t*)attr_rec->value, i, &s);
o=PyString_FromString(s);
break;
default:
IGRAPH_WARNING("unsupported attribute type (not string and not numeric)");
o=0;
break;
}
if (o) {
if (PyList_Append(value, o) == -1)
IGRAPH_ERROR("can't extend a vertex attribute hash member", IGRAPH_FAILURE);
else Py_DECREF(o);
}
}
} else {
for (i=0; i<nv; i++) {
if (PyList_Append(value, Py_None) == -1) {
IGRAPH_ERROR("can't extend a vertex attribute hash member", IGRAPH_FAILURE);
}
}
}
}
/* Okay, now we added the new attribute values for the already existing
* attribute keys. Let's see if we have something left */
if (attr) {
l=igraph_vector_ptr_size(attr);
j=igraph_vcount(graph)-nv;
/* j contains the number of vertices EXCLUDING the new ones! */
for (k=0; k<l; k++) {
if (added_attrs[k]) continue;
attr_rec=(igraph_i_attribute_record_t*)VECTOR(*attr)[k];
value=PyList_New(j + nv);
if (!value) {
IGRAPH_ERROR("can't add attributes", IGRAPH_ENOMEM);
}
for (i=0; i<j; i++) {
Py_INCREF(Py_None);
PyList_SET_ITEM(value, i, Py_None);
}
for (i=0; i<nv; i++) {
char *s;
PyObject *o;
switch (attr_rec->type) {
case IGRAPH_ATTRIBUTE_NUMERIC:
o=PyFloat_FromDouble((double)VECTOR(*(igraph_vector_t*)attr_rec->value)[i]);
break;
case IGRAPH_ATTRIBUTE_STRING:
igraph_strvector_get((igraph_strvector_t*)attr_rec->value, i, &s);
o=PyString_FromString(s);
break;
default:
IGRAPH_WARNING("unsupported attribute type (not string and not numeric)");
o=0;
break;
}
if (o) PyList_SET_ITEM(value, i+j, o);
}
PyDict_SetItemString(dict, attr_rec->name, value);
}
free(added_attrs);
IGRAPH_FINALLY_CLEAN(1);
}
return IGRAPH_SUCCESS;
}
static void igraphmodule_i_attribute_delete_edges(igraph_t *graph, const igraph_vector_t *idx);
/* Deleting vertices */
static void igraphmodule_i_attribute_delete_vertices(igraph_t *graph,
const igraph_vector_t *eidx,
const igraph_vector_t *vidx) {
long int n, i, ndeleted=0;
PyObject *key, *value, *dict, *o;
int pos=0;
/* Reindexing vertices */
dict=((PyObject**)graph->attr)[1];
if (!PyDict_Check(dict)) return;
n=igraph_vector_size(vidx);
for (i=0; i<n; i++) {
/*printf("%ld:%f ", i, VECTOR(*idx)[i]);*/
if (!VECTOR(*vidx)[i]) {
ndeleted++;
continue;
}
pos=0;
/* TODO: maybe it would be more efficient to get the values from the
* hash in advance? */
while (PyDict_Next(dict, &pos, &key, &value)) {
/* Move the element from index i to VECTOR(*idx)[i]-1 */
o=PyList_GetItem(value, i);
if (!o) {
/* IndexError is already set, clear it and return */
PyErr_Clear();
return;
}
Py_INCREF(o);
PyList_SetItem(value, VECTOR(*vidx)[i]-1, o);
}
}
/*printf("\n");*/
/* Clear the remaining parts of the lists that aren't needed anymore */
pos=0;
while (PyDict_Next(dict, &pos, &key, &value)) {
n=PySequence_Size(value);
if (PySequence_DelSlice(value, n-ndeleted, n) == -1) return;
/*printf("key: "); PyObject_Print(key, stdout, Py_PRINT_RAW); printf("\n");
printf("value: "); PyObject_Print(value, stdout, Py_PRINT_RAW); printf("\n");*/
}
igraphmodule_i_attribute_delete_edges(graph, eidx);
return;
}
/* Adding edges */
static int igraphmodule_i_attribute_add_edges(igraph_t *graph, const igraph_vector_t *edges, igraph_vector_ptr_t *attr) {
/* Extend the end of every value in the edge hash with ne pieces of None */
PyObject *key, *value, *dict;
int pos=0;
long int i, j, k, l, ne;
igraph_bool_t *added_attrs=0;
igraph_i_attribute_record_t *attr_rec;
ne=igraph_vector_size(edges)/2;
if (!graph->attr) return IGRAPH_SUCCESS;
if (ne<=0) return IGRAPH_SUCCESS;
if (attr) {
added_attrs = (igraph_bool_t*)calloc((size_t)igraph_vector_ptr_size(attr),
sizeof(igraph_bool_t));
if (!added_attrs)
IGRAPH_ERROR("can't add vertex attributes", IGRAPH_ENOMEM);
IGRAPH_FINALLY(free, added_attrs);
}
dict=((PyObject**)graph->attr)[2];
if (!PyDict_Check(dict))
IGRAPH_ERROR("edge attribute hash type mismatch", IGRAPH_EINVAL);
while (PyDict_Next(dict, &pos, &key, &value)) {
if (!PyString_Check(key))
IGRAPH_ERROR("edge attribute hash key is not a string", IGRAPH_EINVAL);
if (!PyList_Check(value))
IGRAPH_ERROR("edge attribute hash member is not a list", IGRAPH_EINVAL);
/* Check if we have specific values for the given attribute */
attr_rec=0;
if (attr) {
j=igraph_vector_ptr_size(attr);
for (i=0; i<j; i++) {
attr_rec=VECTOR(*attr)[i];
if (!strcmp(attr_rec->name, PyString_AS_STRING(key))) {
added_attrs[i]=1;
break;
}
attr_rec=0;
}
}
/* If we have specific values for the given attribute, attr_rec contains
* the appropriate vector. If not, it is null. */
if (attr_rec) {
for (i=0; i<ne; i++) {
char *s;
PyObject *o;
switch (attr_rec->type) {
case IGRAPH_ATTRIBUTE_NUMERIC:
o=PyFloat_FromDouble((double)VECTOR(*(igraph_vector_t*)attr_rec->value)[i]);
break;
case IGRAPH_ATTRIBUTE_STRING:
igraph_strvector_get((igraph_strvector_t*)attr_rec->value, i, &s);
o=PyString_FromString(s);
break;
default:
IGRAPH_WARNING("unsupported attribute type (not string and not numeric)");
o=0;
break;
}
if (o) {
if (PyList_Append(value, o) == -1)
IGRAPH_ERROR("can't extend an edge attribute hash member", IGRAPH_FAILURE);
else Py_DECREF(o);
}
}
} else {
for (i=0; i<ne; i++) {
if (PyList_Append(value, Py_None) == -1) {
IGRAPH_ERROR("can't extend an edge attribute hash member", IGRAPH_FAILURE);
}
}
}
}
/*pos=0;
while (PyDict_Next(dict, &pos, &key, &value)) {
printf("key: "); PyObject_Print(key, stdout, Py_PRINT_RAW); printf("\n");
printf("value: "); PyObject_Print(value, stdout, Py_PRINT_RAW); printf("\n");
}*/
/* Okay, now we added the new attribute values for the already existing
* attribute keys. Let's see if we have something left */
if (attr) {
l=igraph_vector_ptr_size(attr);
j=igraph_ecount(graph)-ne;
/* j contains the number of edges EXCLUDING the new ones! */
for (k=0; k<l; k++) {
if (added_attrs[k]) continue;
attr_rec=(igraph_i_attribute_record_t*)VECTOR(*attr)[k];
value=PyList_New(j+ne);
if (!value) {
IGRAPH_ERROR("can't add attributes", IGRAPH_ENOMEM);
}
for (i=0; i<j; i++) {
Py_INCREF(Py_None);
PyList_SET_ITEM(value, i, Py_None);
}
for (i=0; i<ne; i++) {
char *s;
PyObject *o;
switch (attr_rec->type) {
case IGRAPH_ATTRIBUTE_NUMERIC:
o=PyFloat_FromDouble((double)VECTOR(*(igraph_vector_t*)attr_rec->value)[i]);
break;
case IGRAPH_ATTRIBUTE_STRING:
igraph_strvector_get((igraph_strvector_t*)attr_rec->value, i, &s);
o=PyString_FromString(s);
break;
default:
IGRAPH_WARNING("unsupported attribute type (not string and not numeric)");
o=0;
break;
}
if (o) PyList_SET_ITEM(value, i+j, o);
}
PyDict_SetItemString(dict, attr_rec->name, value);
}
free(added_attrs);
IGRAPH_FINALLY_CLEAN(1);
}
return IGRAPH_SUCCESS;
}
/* Deleting edges */
static void igraphmodule_i_attribute_delete_edges(igraph_t *graph, const igraph_vector_t *idx) {
long int n, i, ndeleted=0;
PyObject *key, *value, *dict, *o;
int pos=0;
dict=((PyObject**)graph->attr)[2];
if (!PyDict_Check(dict)) return;
n=igraph_vector_size(idx);
for (i=0; i<n; i++) {
/* printf("%ld:%f ", i, VECTOR(*idx)[i]); */
if (!VECTOR(*idx)[i]) {
ndeleted++;
continue;
}
pos=0;
/* TODO: maybe it would be more efficient to get the values from the
* hash in advance? */
while (PyDict_Next(dict, &pos, &key, &value)) {
/* Move the element from index i to VECTOR(*idx)[i]-1 */
o=PyList_GetItem(value, i);
if (!o) {
/* IndexError is already set, clear it and return */
PyErr_Clear();
return;
}
Py_INCREF(o);
PyList_SetItem(value, VECTOR(*idx)[i]-1, o);
}
}
/*printf("\n");*/
/* Clear the remaining parts of the lists that aren't needed anymore */
pos=0;
while (PyDict_Next(dict, &pos, &key, &value)) {
n=PySequence_Size(value);
if (PySequence_DelSlice(value, n-ndeleted, n) == -1) return;
/*printf("key: "); PyObject_Print(key, stdout, Py_PRINT_RAW); printf("\n");
printf("value: "); PyObject_Print(value, stdout, Py_PRINT_RAW); printf("\n");*/
}
return;
}
/* Permuting edges */
static int igraphmodule_i_attribute_permute_edges(igraph_t *graph,
const igraph_vector_t *idx) {
long int n, i;
PyObject *key, *value, *dict, *newdict, *newlist, *o;
int pos=0;
dict=((PyObject**)graph->attr)[2];
if (!PyDict_Check(dict)) return 1;
newdict=PyDict_New();
if (!newdict) return 1;
n=igraph_vector_size(idx);
pos=0;
while (PyDict_Next(dict, &pos, &key, &value)) {
newlist=PyList_New(n);
for (i=0; i<n; i++) {
o=PyList_GetItem(value, VECTOR(*idx)[i]-1);
if (!o) {
PyErr_Clear();
return 1;
}
Py_INCREF(o);
PyList_SET_ITEM(newlist, i, o);
}
PyDict_SetItem(newdict, key, newlist);
Py_DECREF(newlist);
}
((PyObject**)graph->attr)[2]=newdict;
Py_DECREF(dict);
return 0;
}
/* Getting attribute names and types */
static int igraphmodule_i_attribute_get_info(const igraph_t *graph,
igraph_strvector_t *gnames,
igraph_vector_t *gtypes,
igraph_strvector_t *vnames,
igraph_vector_t *vtypes,
igraph_strvector_t *enames,
igraph_vector_t *etypes) {
igraph_strvector_t *names[3] = { gnames, vnames, enames };
igraph_vector_t *types[3] = { gtypes, vtypes, etypes };
long int i, j, k, l, m;
for (i=0; i<3; i++) {
igraph_strvector_t *n = names[i];
igraph_vector_t *t = types[i];
PyObject *dict = ((PyObject**)graph->attr)[i];
PyObject *keys;
PyObject *values;
PyObject *o=0;
keys=PyDict_Keys(dict);
if (!keys) IGRAPH_ERROR("Internal error in PyDict_Keys", IGRAPH_FAILURE);
if (n) {
j=igraphmodule_PyList_to_strvector_t(keys, n);
if (j) return j;
}
if (t) {
k=PyList_Size(keys);
igraph_vector_init(t, k);
for (j=0; j<k; j++) {
int is_numeric = 1;
values=PyDict_GetItem(dict, PyList_GetItem(keys, j));
if (PyList_Check(values)) {
m=PyList_Size(values);
for (l=0; l<m && is_numeric; l++) {
o=PyList_GetItem(values, l);
if (o != Py_None && !PyNumber_Check(o)) is_numeric=0;
}
} else if (o != Py_None && !PyNumber_Check(values)) is_numeric=0;
VECTOR(*t)[j]=is_numeric ? IGRAPH_ATTRIBUTE_NUMERIC : IGRAPH_ATTRIBUTE_STRING;
}
}
Py_DECREF(keys);
}
return 0;
}
/* Checks whether the graph has a graph/vertex/edge attribute with the given name */
igraph_bool_t igraphmodule_i_attribute_has_attr(const igraph_t *graph,
igraph_attribute_elemtype_t type,
const char* name) {
long int attrnum;
PyObject *o, *dict;
switch (type) {
case IGRAPH_ATTRIBUTE_GRAPH: attrnum=0; break;
case IGRAPH_ATTRIBUTE_VERTEX: attrnum=1; break;
case IGRAPH_ATTRIBUTE_EDGE: attrnum=2; break;
default: return 0; break;
}
dict = ((PyObject**)graph->attr)[attrnum];
o = PyDict_GetItemString(dict, name);
return o != 0;
}
/* Returns the type of a given attribute */
int igraphmodule_i_attribute_get_type(const igraph_t *graph,
igraph_attribute_type_t *type,
igraph_attribute_elemtype_t elemtype,
const char *name) {
long int attrnum, i, j;
int is_numeric;
PyObject *o, *dict;
switch (elemtype) {
case IGRAPH_ATTRIBUTE_GRAPH: attrnum=0; break;
case IGRAPH_ATTRIBUTE_VERTEX: attrnum=1; break;
case IGRAPH_ATTRIBUTE_EDGE: attrnum=2; break;
default: IGRAPH_ERROR("No such attribute type", IGRAPH_EINVAL); break;
}
dict = ((PyObject**)graph->attr)[attrnum];
o = PyDict_GetItemString(dict, name);
if (o == 0) IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL);
is_numeric = 1;
if (attrnum>0) {
if (!PyList_Check(o)) IGRAPH_ERROR("attribute hash type mismatch", IGRAPH_EINVAL);
if (!PyList_Size(o)) IGRAPH_ERROR("attribute hash type mismatch", IGRAPH_EINVAL);
j = PyList_Size(o);
for (i=0; i<j && is_numeric; i++)
if (o != Py_None && !PyNumber_Check(o)) is_numeric=0;
} else if (o != Py_None && !PyNumber_Check(o)) is_numeric=0;
if (is_numeric)
*type = IGRAPH_ATTRIBUTE_NUMERIC;
else
*type = IGRAPH_ATTRIBUTE_STRING;
return 0;
}
/* Getting numeric graph attributes */
int igraphmodule_i_get_numeric_graph_attr(const igraph_t *graph,
const char *name, igraph_vector_t *value) {
PyObject *dict, *o, *result;
dict = ((PyObject**)graph->attr)[0];
/* No error checking, if we get here, the type has already been checked by previous
attribute handler calls... hopefully :) Same applies for the other handlers. */
o = PyDict_GetItemString(dict, name);
if (!o) IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL);
IGRAPH_CHECK(igraph_vector_resize(value, 1));
if (o == Py_None) {
VECTOR(*value)[0] = NAN;
return 0;
}
result = PyNumber_Float(o);
if (result) {
VECTOR(*value)[0] = PyFloat_AsDouble(o);
Py_DECREF(result);
} else IGRAPH_ERROR("Internal error in PyFloat_AsDouble", IGRAPH_EINVAL);
return 0;
}
/* Getting string graph attributes */
int igraphmodule_i_get_string_graph_attr(const igraph_t *graph,
const char *name, igraph_strvector_t *value) {
PyObject *dict, *o, *result;
dict = ((PyObject**)graph->attr)[0];
o = PyDict_GetItemString(dict, name);
if (!o) IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL);
IGRAPH_CHECK(igraph_strvector_resize(value, 1));
result = PyObject_Str(o);
if (result) {
IGRAPH_CHECK(igraph_strvector_set(value, 0, PyString_AsString(result)));
Py_DECREF(result);
} else IGRAPH_ERROR("Internal error in PyObject_Str", IGRAPH_EINVAL);
return 0;
}
/* Getting numeric vertex attributes */
int igraphmodule_i_get_numeric_vertex_attr(const igraph_t *graph,
const char *name,
igraph_vs_t vs,
igraph_vector_t *value) {
PyObject *dict, *list, *result, *o;
igraph_vector_t newvalue;
dict = ((PyObject**)graph->attr)[1];
list = PyDict_GetItemString(dict, name);
if (!list) IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL);
if (igraph_vs_is_all(&vs)) {
if (igraphmodule_PyList_to_vector_t(list, &newvalue, 0, 0))
IGRAPH_ERROR("Internal error", IGRAPH_EINVAL);
igraph_vector_destroy(value);
*value=newvalue;
} else {
igraph_vit_t it;
long int i=0;
IGRAPH_CHECK(igraph_vit_create(graph, vs, &it));
IGRAPH_FINALLY(igraph_vit_destroy, &it);
IGRAPH_CHECK(igraph_vector_resize(value, IGRAPH_VIT_SIZE(it)));
while (!IGRAPH_VIT_END(it)) {
long int v=IGRAPH_VIT_GET(it);
o = PyList_GetItem(list, v);
if (o != Py_None) {
result = PyNumber_Float(o);
VECTOR(*value)[i] = PyFloat_AsDouble(result);
Py_XDECREF(result);
} else VECTOR(*value)[i] = NAN;
IGRAPH_VIT_NEXT(it);
i++;
}
igraph_vit_destroy(&it);
IGRAPH_FINALLY_CLEAN(1);
}
return 0;
}
/* Getting string vertex attributes */
int igraphmodule_i_get_string_vertex_attr(const igraph_t *graph,
const char *name,
igraph_vs_t vs,
igraph_strvector_t *value) {
PyObject *dict, *list, *result;
igraph_strvector_t newvalue;
dict = ((PyObject**)graph->attr)[1];
list = PyDict_GetItemString(dict, name);
if (!list) IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL);
if (igraph_vs_is_all(&vs)) {
if (igraphmodule_PyList_to_strvector_t(list, &newvalue))
IGRAPH_ERROR("Internal error", IGRAPH_EINVAL);
igraph_strvector_destroy(value);
*value=newvalue;
} else {
igraph_vit_t it;
long int i=0;
IGRAPH_CHECK(igraph_vit_create(graph, vs, &it));
IGRAPH_FINALLY(igraph_vit_destroy, &it);
IGRAPH_CHECK(igraph_strvector_resize(value, IGRAPH_VIT_SIZE(it)));
while (!IGRAPH_VIT_END(it)) {
long int v=IGRAPH_VIT_GET(it);
result = PyObject_Str(PyList_GetItem(list, v));
igraph_strvector_set(value, i, PyString_AsString(result));
Py_XDECREF(result);
IGRAPH_VIT_NEXT(it);
i++;
}
igraph_vit_destroy(&it);
IGRAPH_FINALLY_CLEAN(1);
}
return 0;
}
/* Getting numeric edge attributes */
int igraphmodule_i_get_numeric_edge_attr(const igraph_t *graph,
const char *name,
igraph_es_t es,
igraph_vector_t *value) {
PyObject *dict, *list, *result, *o;
igraph_vector_t newvalue;
dict = ((PyObject**)graph->attr)[2];
list = PyDict_GetItemString(dict, name);
if (!list) IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL);
if (igraph_es_is_all(&es)) {
if (igraphmodule_PyList_to_vector_t(list, &newvalue, 0, 0))
IGRAPH_ERROR("Internal error", IGRAPH_EINVAL);
igraph_vector_destroy(value);
*value=newvalue;
} else {
igraph_eit_t it;
long int i=0;
IGRAPH_CHECK(igraph_eit_create(graph, es, &it));
IGRAPH_FINALLY(igraph_eit_destroy, &it);
IGRAPH_CHECK(igraph_vector_resize(value, IGRAPH_EIT_SIZE(it)));
while (!IGRAPH_EIT_END(it)) {
long int v=IGRAPH_EIT_GET(it);
o = PyList_GetItem(list, v);
if (o != Py_None) {
result = PyNumber_Float(o);
VECTOR(*value)[i] = PyFloat_AsDouble(result);
Py_XDECREF(result);
} else VECTOR(*value)[i] = NAN;
IGRAPH_EIT_NEXT(it);
i++;
}
igraph_eit_destroy(&it);
IGRAPH_FINALLY_CLEAN(1);
}
return 0;
}
/* Getting string edge attributes */
int igraphmodule_i_get_string_edge_attr(const igraph_t *graph,
const char *name,
igraph_es_t es,
igraph_strvector_t *value) {
PyObject *dict, *list, *result;
igraph_strvector_t newvalue;
dict = ((PyObject**)graph->attr)[2];
list = PyDict_GetItemString(dict, name);
if (!list) IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL);
if (igraph_es_is_all(&es)) {
if (igraphmodule_PyList_to_strvector_t(list, &newvalue))
IGRAPH_ERROR("Internal error", IGRAPH_EINVAL);
igraph_strvector_destroy(value);
*value=newvalue;
} else {
igraph_eit_t it;
long int i=0;
IGRAPH_CHECK(igraph_eit_create(graph, es, &it));
IGRAPH_FINALLY(igraph_eit_destroy, &it);
IGRAPH_CHECK(igraph_strvector_resize(value, IGRAPH_EIT_SIZE(it)));
while (!IGRAPH_EIT_END(it)) {
long int v=IGRAPH_EIT_GET(it);
result = PyObject_Str(PyList_GetItem(list, v));
igraph_strvector_set(value, i, PyString_AsString(result));
Py_XDECREF(result);
IGRAPH_EIT_NEXT(it);
i++;
}
igraph_eit_destroy(&it);
IGRAPH_FINALLY_CLEAN(1);
}
return 0;
}
static igraph_attribute_table_t igraphmodule_i_attribute_table = {
igraphmodule_i_attribute_init,
igraphmodule_i_attribute_destroy,
igraphmodule_i_attribute_copy,
igraphmodule_i_attribute_add_vertices,
igraphmodule_i_attribute_delete_vertices,
igraphmodule_i_attribute_add_edges,
igraphmodule_i_attribute_delete_edges,
igraphmodule_i_attribute_permute_edges,
igraphmodule_i_attribute_get_info,
igraphmodule_i_attribute_has_attr,
igraphmodule_i_attribute_get_type,
igraphmodule_i_get_numeric_graph_attr,
igraphmodule_i_get_string_graph_attr,
igraphmodule_i_get_numeric_vertex_attr,
igraphmodule_i_get_string_vertex_attr,
igraphmodule_i_get_numeric_edge_attr,
igraphmodule_i_get_string_edge_attr,
};
/** \ingroup python_interface
* \brief Method table for the igraph Python module
*/
static PyMethodDef igraphmodule_methods[] =
{
{"convex_hull", (PyCFunction)igraphmodule_convex_hull, METH_VARARGS,
"convex_hull(vs, coords=False)\n\n"
"Calculates the convex hull of a given point set.\n\n"
"@param vs: the point set as a list of lists\n"
"@param coords: if C{True}, the function returns the\n"
" coordinates of the corners of the convex hull polygon,\n"
" otherwise returns the corner indices.\n"
"@return: either the hull's corner coordinates or the point\n"
" indices corresponding to them, depending on the C{coords}\n"
" parameter."
},
{"set_progress_handler", igraphmodule_set_progress_handler, METH_VARARGS,
"set_progress_handler(handler)\n\n"
"Sets the handler to be called when igraph is performing a long operation.\n"
"@param handler: the progress handler function. It must accept two\n"
" arguments, the first is the message informing the user about\n"
" what igraph is doing right now, the second is the actual\n"
" progress information (a percentage).\n"
},
{NULL, NULL, 0, NULL}
};
#ifndef PyMODINIT_FUNC
#define PyMODINIT_FUNC void
#endif
extern PyObject* igraphmodule_InternalError;
PyMODINIT_FUNC initcore(void) {
PyObject* m;
igraphmodule_VertexSeqType.tp_traverse = (traverseproc)igraphmodule_VertexSeq_traverse;
igraphmodule_VertexSeqType.tp_clear = (inquiry)igraphmodule_VertexSeq_clear;
if (PyType_Ready(&igraphmodule_VertexSeqType) < 0) return;
igraphmodule_VertexType.tp_traverse = (traverseproc)igraphmodule_Vertex_traverse;
igraphmodule_VertexType.tp_clear = (inquiry)igraphmodule_Vertex_clear;
if (PyType_Ready(&igraphmodule_VertexType) < 0) return;
igraphmodule_EdgeSeqType.tp_traverse = (traverseproc)igraphmodule_EdgeSeq_traverse;
igraphmodule_EdgeSeqType.tp_clear = (inquiry)igraphmodule_EdgeSeq_clear;
if (PyType_Ready(&igraphmodule_EdgeSeqType) < 0) return;
igraphmodule_EdgeType.tp_traverse = (traverseproc)igraphmodule_Edge_traverse;
igraphmodule_EdgeType.tp_clear = (inquiry)igraphmodule_Edge_clear;
if (PyType_Ready(&igraphmodule_EdgeType) < 0) return;
if (PyType_Ready(&igraphmodule_GraphType) < 0) return;
if (PyType_Ready(&igraphmodule_BFSIterType) < 0) return;
igraphmodule_InternalError =
PyErr_NewException("igraph.core.InternalError", PyExc_Exception, NULL);
Py_INCREF(igraphmodule_InternalError);
m = Py_InitModule3("igraph.core", igraphmodule_methods,
"Low-level Python interface for the igraph library. "
"Should not be used directly.");
Py_INCREF(&igraphmodule_GraphType);
PyModule_AddObject(m, "GraphBase", (PyObject*)&igraphmodule_GraphType);
PyModule_AddObject(m, "BFSIter", (PyObject*)&igraphmodule_BFSIterType);
/* These types are not necessary to be registered, but I want epydoc to
* see them so the proper documentation can be generated for them */
PyModule_AddObject(m, "Edge", (PyObject*)&igraphmodule_EdgeType);
PyModule_AddObject(m, "EdgeSeq", (PyObject*)&igraphmodule_EdgeSeqType);
PyModule_AddObject(m, "Vertex", (PyObject*)&igraphmodule_VertexType);
PyModule_AddObject(m, "VertexSeq", (PyObject*)&igraphmodule_VertexSeqType);
PyModule_AddObject(m, "InternalError", igraphmodule_InternalError);
PyModule_AddIntConstant(m, "OUT", IGRAPH_OUT);
PyModule_AddIntConstant(m, "IN", IGRAPH_IN);
PyModule_AddIntConstant(m, "ALL", IGRAPH_ALL);
PyModule_AddIntConstant(m, "STAR_OUT", IGRAPH_STAR_OUT);
PyModule_AddIntConstant(m, "STAR_IN", IGRAPH_STAR_IN);
PyModule_AddIntConstant(m, "STAR_UNDIRECTED", IGRAPH_STAR_UNDIRECTED);
PyModule_AddIntConstant(m, "TREE_OUT", IGRAPH_TREE_OUT);
PyModule_AddIntConstant(m, "TREE_IN", IGRAPH_TREE_IN);
PyModule_AddIntConstant(m, "TREE_UNDIRECTED", IGRAPH_TREE_UNDIRECTED);
PyModule_AddIntConstant(m, "STRONG", IGRAPH_STRONG);
PyModule_AddIntConstant(m, "WEAK", IGRAPH_WEAK);
PyModule_AddIntConstant(m, "GET_ADJACENCY_UPPER", IGRAPH_GET_ADJACENCY_UPPER);
PyModule_AddIntConstant(m, "GET_ADJACENCY_LOWER", IGRAPH_GET_ADJACENCY_LOWER);
PyModule_AddIntConstant(m, "GET_ADJACENCY_BOTH", IGRAPH_GET_ADJACENCY_BOTH);
PyModule_AddIntConstant(m, "REWIRING_SIMPLE", IGRAPH_REWIRING_SIMPLE);
PyModule_AddIntConstant(m, "ADJ_DIRECTED", IGRAPH_ADJ_DIRECTED);
PyModule_AddIntConstant(m, "ADJ_UNDIRECTED", IGRAPH_ADJ_UNDIRECTED);
PyModule_AddIntConstant(m, "ADJ_MAX", IGRAPH_ADJ_MAX);
PyModule_AddIntConstant(m, "ADJ_MIN", IGRAPH_ADJ_MIN);
PyModule_AddIntConstant(m, "ADJ_PLUS", IGRAPH_ADJ_PLUS);
PyModule_AddIntConstant(m, "ADJ_UPPER", IGRAPH_ADJ_UPPER);
PyModule_AddIntConstant(m, "ADJ_LOWER", IGRAPH_ADJ_LOWER);
PyModule_AddStringConstant(m, "__version__", VERSION);
PyModule_AddStringConstant(m, "__build_date__", __DATE__);
/* initialize error, progress and interruption handler */
igraph_set_error_handler(igraphmodule_igraph_error_hook);
igraph_set_progress_handler(igraphmodule_igraph_progress_hook);
igraph_set_interruption_handler(igraphmodule_igraph_interrupt_hook);
/* initialize attribute handlers */
igraph_i_set_attribute_table(&igraphmodule_i_attribute_table);
}
syntax highlighted by Code2HTML, v. 0.9.1