~ubuntu-branches/ubuntu/vivid/python-igraph/vivid

« back to all changes in this revision

Viewing changes to src/igraphmodule.c

  • Committer: Package Import Robot
  • Author(s): TANIGUCHI Takaki
  • Date: 2012-03-17 17:23:55 UTC
  • Revision ID: package-import@ubuntu.com-20120317172355-e9iki37igmxnlq38
Tags: upstream-0.5.4
ImportĀ upstreamĀ versionĀ 0.5.4

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* vim:set ts=2 sw=2 sts=2 et: */
 
2
/* 
 
3
   IGraph library.
 
4
   Copyright (C) 2006  Gabor Csardi <csardi@rmki.kfki.hu>
 
5
   MTA RMKI, Konkoly-Thege Miklos st. 29-33, Budapest 1121, Hungary
 
6
   
 
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.
 
11
   
 
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.
 
16
   
 
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 
 
20
   02110-1301 USA
 
21
 
 
22
*/
 
23
 
 
24
#include <Python.h>
 
25
#include <pythonrun.h>
 
26
#include <igraph/igraph.h>
 
27
#include "common.h"
 
28
#include "error.h"
 
29
#include "convert.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"
 
36
#include "bfsiter.h"
 
37
//#include "config.h"
 
38
 
 
39
extern double igraph_i_fdiv(double, double);
 
40
 
 
41
/**
 
42
 * \defgroup python_interface Python module implementation
 
43
 * \brief Functions implementing a Python interface to \a igraph
 
44
 * 
 
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):
 
50
 * 
 
51
 * \verbatim
 
52
import igraph
 
53
help(igraph)
 
54
help(igraph.Graph)
 
55
\endverbatim
 
56
 * 
 
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.
 
61
 * 
 
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.
 
67
 * 
 
68
 * Any arguments not documented here should be mentioned at the documentation
 
69
 * of the appropriate method.
 
70
 * 
 
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
 
78
 * it explicitly.
 
79
 * 
 
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.
 
86
 *
 
87
 * \section weakrefs The usage of weak references in the Python interface
 
88
 * 
 
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:
 
92
 * 
 
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
 
110
 *    references.
 
111
 */
 
112
 
 
113
static PyObject* igraphmodule_progress_handler=NULL;
 
114
 
 
115
static int igraphmodule_igraph_interrupt_hook(void* data) {
 
116
  if (PyErr_CheckSignals()) {
 
117
    IGRAPH_FINALLY_FREE();
 
118
    return IGRAPH_INTERRUPTED;
 
119
  }
 
120
  return IGRAPH_SUCCESS;
 
121
}
 
122
 
 
123
int igraphmodule_igraph_progress_hook(const char* message, igraph_real_t percent,
 
124
                                       void* data) {
 
125
  if (igraphmodule_progress_handler) {
 
126
    PyObject *result;
 
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;
 
132
    }
 
133
  }
 
134
  
 
135
  return IGRAPH_SUCCESS;
 
136
}
 
137
 
 
138
PyObject* igraphmodule_set_progress_handler(PyObject* self, PyObject* args) {
 
139
  PyObject* o;
 
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.");
 
143
    return NULL;
 
144
  }
 
145
  Py_XDECREF(igraphmodule_progress_handler);
 
146
  if (o == Py_None)
 
147
     igraphmodule_progress_handler=NULL;
 
148
  else {
 
149
    Py_INCREF(o);
 
150
    igraphmodule_progress_handler=o;
 
151
  }
 
152
  Py_RETURN_NONE;
 
153
}
 
154
 
 
155
 
 
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;
 
162
  long no_of_nodes, i;
 
163
  
 
164
  if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|O", kwlist, &PyList_Type, &vs, &coords))
 
165
    return NULL;
 
166
  
 
167
  no_of_nodes=PyList_Size(vs);
 
168
  if (igraph_matrix_init(&mtrx, no_of_nodes, 2)) {
 
169
    igraphmodule_handle_igraph_error();
 
170
    return NULL;
 
171
  }
 
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");
 
180
      } else {
 
181
        PyErr_SetString(PyExc_TypeError, "vertex with less than 2 coordinates found");
 
182
        igraph_matrix_destroy(&mtrx);
 
183
            return NULL;
 
184
      }
 
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");
 
191
       } else {
 
192
              PyErr_SetString(PyExc_TypeError, "vertex with less than 2 coordinates found");
 
193
            igraph_matrix_destroy(&mtrx);
 
194
            return NULL;
 
195
      }
 
196
    }
 
197
    
 
198
    if (!PyNumber_Check(o1) || !PyNumber_Check(o2)) {
 
199
      PyErr_SetString(PyExc_TypeError, "vertex coordinates must be numeric");
 
200
      igraph_matrix_destroy(&mtrx);
 
201
      return NULL;
 
202
    }
 
203
    /* o, o1 and o2 were borrowed, but now o1 and o2 are actual references! */
 
204
    o1=PyNumber_Float(o1); o2=PyNumber_Float(o2);
 
205
    if (!o1 || !o2) {
 
206
      PyErr_SetString(PyExc_TypeError, "vertex coordinate conversion to float failed");
 
207
      Py_XDECREF(o1);
 
208
      Py_XDECREF(o2);
 
209
      igraph_matrix_destroy(&mtrx);
 
210
      return NULL;
 
211
    }
 
212
    MATRIX(mtrx, i, 0)=(igraph_real_t)PyFloat_AsDouble(o1);
 
213
    MATRIX(mtrx, i, 1)=(igraph_real_t)PyFloat_AsDouble(o2);
 
214
    Py_DECREF(o1);
 
215
    Py_DECREF(o2);
 
216
  }
 
217
 
 
218
  if (!PyObject_IsTrue(coords)) {
 
219
    if (igraph_vector_init(&result, 0)) {
 
220
      igraphmodule_handle_igraph_error();
 
221
      igraph_matrix_destroy(&mtrx);
 
222
      return NULL;
 
223
    }
 
224
    if (igraph_convex_hull(&mtrx, &result, 0)) {
 
225
      igraphmodule_handle_igraph_error();
 
226
      igraph_matrix_destroy(&mtrx);
 
227
      igraph_vector_destroy(&result);
 
228
      return NULL;
 
229
    }    
 
230
    o=igraphmodule_vector_t_to_PyList(&result, IGRAPHMODULE_TYPE_INT);
 
231
    igraph_vector_destroy(&result);
 
232
  } else {
 
233
    if (igraph_matrix_init(&resmat, 0, 0)) {
 
234
      igraphmodule_handle_igraph_error();
 
235
      igraph_matrix_destroy(&mtrx);
 
236
      return NULL;
 
237
    }
 
238
    if (igraph_convex_hull(&mtrx, 0, &resmat)) {
 
239
      igraphmodule_handle_igraph_error();
 
240
      igraph_matrix_destroy(&mtrx);
 
241
      igraph_matrix_destroy(&resmat);
 
242
      return NULL;
 
243
    }        
 
244
    o=igraphmodule_matrix_t_to_PyList(&resmat, IGRAPHMODULE_TYPE_FLOAT);
 
245
    igraph_matrix_destroy(&resmat);
 
246
  }
 
247
  
 
248
  igraph_matrix_destroy(&mtrx);
 
249
 
 
250
  return o;
 
251
}
 
252
 
 
253
 
 
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;
 
261
 
 
262
  if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!ll|O", kwlist,
 
263
      &PyList_Type, &merges_o, &nodes, &steps, &return_csize)) return NULL;
 
264
 
 
265
  if (igraphmodule_PyList_to_matrix_t(merges_o, &merges)) return NULL;
 
266
 
 
267
  if (igraph_vector_init(&result, nodes)) {
 
268
    igraphmodule_handle_igraph_error();
 
269
    igraph_matrix_destroy(&merges);
 
270
    return NULL;
 
271
  }
 
272
 
 
273
  if (PyObject_IsTrue(return_csize)) {
 
274
        igraph_vector_init(&csize, 0);
 
275
        csize_p = &csize;
 
276
  }
 
277
 
 
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);
 
283
    return NULL;
 
284
  }
 
285
  igraph_matrix_destroy(&merges);
 
286
 
 
287
  result_o = igraphmodule_vector_t_to_PyList(&result, IGRAPHMODULE_TYPE_INT);
 
288
  igraph_vector_destroy(&result);
 
289
 
 
290
  if (csize_p) {
 
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);
 
294
        Py_DECREF(result_o);
 
295
        return NULL;
 
296
  }
 
297
 
 
298
  return result_o;
 
299
}
 
300
 
 
301
/* Attribute handlers for the Python interface */
 
302
 
 
303
/* Initialization */ 
 
304
static int igraphmodule_i_attribute_init(igraph_t *graph, igraph_vector_ptr_t *attr) {
 
305
  PyObject** attrs;
 
306
  long int i, n;
 
307
  
 
308
  attrs=(PyObject**)calloc(3, sizeof(PyObject*));
 
309
  if (!attrs)
 
310
    IGRAPH_ERROR("not enough memory to allocate attribute hashes", IGRAPH_ENOMEM);
 
311
  
 
312
  for (i=0; i<3; i++) {
 
313
    attrs[i] = PyDict_New();
 
314
    RC_ALLOC("dict", attrs[i]);
 
315
  }
 
316
  graph->attr=(void*)attrs;
 
317
 
 
318
  /* See if we have graph attributes */
 
319
  if (attr) {
 
320
    PyObject *dict=attrs[ATTRHASH_IDX_GRAPH], *value;
 
321
    char *s;
 
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]);
 
329
        break;
 
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);
 
334
        break;
 
335
      default:
 
336
        IGRAPH_WARNING("unsupported attribute type (not string and not numeric)");
 
337
        value=0;
 
338
        break;
 
339
      }
 
340
      if (value) {
 
341
        if (PyDict_SetItemString(dict, attr_rec->name, value)) {
 
342
          Py_DECREF(value);
 
343
          Py_DECREF(attrs[0]);
 
344
          Py_DECREF(attrs[1]);
 
345
          Py_DECREF(attrs[2]);
 
346
          free(graph->attr); graph->attr = 0;
 
347
          IGRAPH_ERROR("failed to add attributes to graph attribute hash",
 
348
                       IGRAPH_FAILURE);
 
349
        }
 
350
        Py_DECREF(value);
 
351
        value=0;
 
352
      }
 
353
    }
 
354
  }
 
355
 
 
356
  return IGRAPH_SUCCESS;
 
357
}
 
358
 
 
359
/* Destruction */
 
360
static void igraphmodule_i_attribute_destroy(igraph_t *graph) {
 
361
  PyObject** attrs;
 
362
  int i;
 
363
 
 
364
  /* printf("Destroying attribute table\n"); */
 
365
  if (graph->attr) {
 
366
    attrs=(PyObject**)graph->attr;
 
367
    for (i=0; i<3; i++) {
 
368
      Py_DECREF(attrs[i]);
 
369
    }
 
370
    free(attrs);
 
371
  }
 
372
}
 
373
 
 
374
/* Copying */
 
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 };
 
379
  int i, j;
 
380
  Py_ssize_t pos = 0;
 
381
 
 
382
  if (from->attr) {
 
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]);
 
390
        continue;
 
391
      }
 
392
 
 
393
      if (!PyDict_Check(fromattrs[i])) {
 
394
        toattrs[i]=fromattrs[i];
 
395
        Py_XINCREF(o);
 
396
        continue;
 
397
      }
 
398
      
 
399
      toattrs[i]=PyDict_New();
 
400
      RC_ALLOC("dict (copying)", toattrs[i]);
 
401
      
 
402
      pos=0;
 
403
      while (PyDict_Next(fromattrs[i], &pos, &key, &value)) {
 
404
        /* value is only borrowed, so copy it */
 
405
        if (i>0) {
 
406
          newval=PyList_New(PyList_GET_SIZE(value));
 
407
          for (j=0; j<PyList_GET_SIZE(value); j++) {
 
408
            o=PyList_GetItem(value, j);
 
409
            Py_INCREF(o);
 
410
            PyList_SetItem(newval, j, o);
 
411
          }
 
412
        } else {
 
413
          newval=value;
 
414
          Py_INCREF(newval);
 
415
        }
 
416
        PyDict_SetItem(toattrs[i], key, newval);
 
417
        Py_DECREF(newval); /* compensate for PyDict_SetItem */
 
418
      }
 
419
    }
 
420
  }
 
421
  return IGRAPH_SUCCESS;
 
422
}
 
423
 
 
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;
 
428
  long int i, j, k, l;
 
429
  igraph_i_attribute_record_t *attr_rec;
 
430
  igraph_bool_t *added_attrs=0;
 
431
  Py_ssize_t pos = 0;
 
432
 
 
433
  if (!graph->attr) return IGRAPH_SUCCESS;
 
434
  if (nv<0) return IGRAPH_SUCCESS;
 
435
 
 
436
  if (attr) {
 
437
    added_attrs = (igraph_bool_t*)calloc((size_t)igraph_vector_ptr_size(attr),
 
438
                                         sizeof(igraph_bool_t));
 
439
    if (!added_attrs)
 
440
      IGRAPH_ERROR("can't add vertex attributes", IGRAPH_ENOMEM);
 
441
    IGRAPH_FINALLY(free, added_attrs);
 
442
  }
 
443
 
 
444
  dict=((PyObject**)graph->attr)[1];
 
445
  if (!PyDict_Check(dict)) 
 
446
    IGRAPH_ERROR("vertex attribute hash type mismatch", IGRAPH_EINVAL);
 
447
 
 
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 */
 
454
    attr_rec=0;
 
455
    if (attr) {
 
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))) {
 
460
          added_attrs[i]=1;
 
461
          break;
 
462
        }
 
463
        attr_rec=0;
 
464
      }
 
465
    }
 
466
    /* If we have specific values for the given attribute, attr_rec contains
 
467
     * the appropriate vector. If not, it is null. */
 
468
    if (attr_rec) {
 
469
      for (i=0; i<nv; i++) {
 
470
        char *s;
 
471
        PyObject *o;
 
472
        switch (attr_rec->type) {
 
473
        case IGRAPH_ATTRIBUTE_NUMERIC:
 
474
          o=PyFloat_FromDouble((double)VECTOR(*(igraph_vector_t*)attr_rec->value)[i]);
 
475
          break;
 
476
        case IGRAPH_ATTRIBUTE_STRING:
 
477
          igraph_strvector_get((igraph_strvector_t*)attr_rec->value, i, &s);
 
478
          o=PyString_FromString(s);
 
479
          break;
 
480
        default:
 
481
          IGRAPH_WARNING("unsupported attribute type (not string and not numeric)");
 
482
          o=0;
 
483
          break;
 
484
        }
 
485
        if (o) {
 
486
          if (PyList_Append(value, o) == -1)
 
487
            IGRAPH_ERROR("can't extend a vertex attribute hash member", IGRAPH_FAILURE);
 
488
          else Py_DECREF(o);
 
489
        }
 
490
      }
 
491
    } else {
 
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);
 
495
        }
 
496
      }
 
497
    }
 
498
  }
 
499
 
 
500
  /* Okay, now we added the new attribute values for the already existing
 
501
   * attribute keys. Let's see if we have something left */
 
502
  if (attr) {
 
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];
 
509
 
 
510
      value=PyList_New(j + nv);
 
511
      if (!value) {
 
512
        IGRAPH_ERROR("can't add attributes", IGRAPH_ENOMEM);
 
513
      }
 
514
 
 
515
      for (i=0; i<j; i++) {
 
516
        Py_INCREF(Py_None);
 
517
        PyList_SET_ITEM(value, i, Py_None);
 
518
      }
 
519
 
 
520
      for (i=0; i<nv; i++) {
 
521
        char *s;
 
522
        PyObject *o;
 
523
        switch (attr_rec->type) {
 
524
        case IGRAPH_ATTRIBUTE_NUMERIC:
 
525
          o=PyFloat_FromDouble((double)VECTOR(*(igraph_vector_t*)attr_rec->value)[i]);
 
526
          break;
 
527
        case IGRAPH_ATTRIBUTE_STRING:
 
528
          igraph_strvector_get((igraph_strvector_t*)attr_rec->value, i, &s);
 
529
          o=PyString_FromString(s);
 
530
          break;
 
531
        default:
 
532
          IGRAPH_WARNING("unsupported attribute type (not string and not numeric)");
 
533
          o=0;
 
534
          break;
 
535
        }
 
536
        if (o) PyList_SET_ITEM(value, i+j, o);
 
537
      }
 
538
 
 
539
      PyDict_SetItemString(dict, attr_rec->name, value);
 
540
      Py_DECREF(value);   /* compensate for PyDict_SetItemString */
 
541
    }
 
542
    free(added_attrs);
 
543
    IGRAPH_FINALLY_CLEAN(1);
 
544
  }
 
545
 
 
546
  return IGRAPH_SUCCESS;
 
547
}
 
548
 
 
549
static void igraphmodule_i_attribute_delete_edges(igraph_t *graph, const igraph_vector_t *idx);
 
550
 
 
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;
 
557
  Py_ssize_t pos=0;
 
558
  
 
559
  /* Reindexing vertices */
 
560
  dict=((PyObject**)graph->attr)[1];
 
561
  if (!PyDict_Check(dict)) return;
 
562
 
 
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]) {
 
567
      ndeleted++;
 
568
      continue;
 
569
    }
 
570
 
 
571
    pos=0;
 
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);
 
577
      if (!o) {
 
578
        /* IndexError is already set, clear it and return */
 
579
        PyErr_Clear();
 
580
        return;
 
581
      }
 
582
      Py_INCREF(o);   /* take ownership, since PyList_SetItem will steal it */
 
583
      PyList_SetItem(value, VECTOR(*vidx)[i]-1, o);
 
584
    }
 
585
  }
 
586
  /*printf("\n");*/
 
587
  
 
588
  /* Clear the remaining parts of the lists that aren't needed anymore */
 
589
  pos=0;
 
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");*/
 
595
  }
 
596
  
 
597
  igraphmodule_i_attribute_delete_edges(graph, eidx);
 
598
 
 
599
  return;
 
600
}
 
601
 
 
602
/* Adding edges */
 
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;
 
606
  Py_ssize_t pos=0;
 
607
  long int i, j, k, l, ne;
 
608
  igraph_bool_t *added_attrs=0;
 
609
  igraph_i_attribute_record_t *attr_rec;
 
610
 
 
611
  ne=igraph_vector_size(edges)/2;
 
612
  if (!graph->attr) return IGRAPH_SUCCESS;
 
613
  if (ne<0) return IGRAPH_SUCCESS;
 
614
  
 
615
  if (attr) {
 
616
    added_attrs = (igraph_bool_t*)calloc((size_t)igraph_vector_ptr_size(attr),
 
617
                                         sizeof(igraph_bool_t));
 
618
    if (!added_attrs)
 
619
      IGRAPH_ERROR("can't add vertex attributes", IGRAPH_ENOMEM);
 
620
    IGRAPH_FINALLY(free, added_attrs);
 
621
  }
 
622
 
 
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);
 
631
 
 
632
    /* Check if we have specific values for the given attribute */
 
633
    attr_rec=0;
 
634
    if (attr) {
 
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))) {
 
639
          added_attrs[i]=1;
 
640
          break;
 
641
        }
 
642
        attr_rec=0;
 
643
      }
 
644
    }
 
645
    /* If we have specific values for the given attribute, attr_rec contains
 
646
     * the appropriate vector. If not, it is null. */
 
647
    if (attr_rec) {
 
648
      for (i=0; i<ne; i++) {
 
649
        char *s;
 
650
        PyObject *o;
 
651
        switch (attr_rec->type) {
 
652
        case IGRAPH_ATTRIBUTE_NUMERIC:
 
653
          o=PyFloat_FromDouble((double)VECTOR(*(igraph_vector_t*)attr_rec->value)[i]);
 
654
          break;
 
655
        case IGRAPH_ATTRIBUTE_STRING:
 
656
          igraph_strvector_get((igraph_strvector_t*)attr_rec->value, i, &s);
 
657
          o=PyString_FromString(s);
 
658
          break;
 
659
        default:
 
660
          IGRAPH_WARNING("unsupported attribute type (not string and not numeric)");
 
661
          o=0;
 
662
          break;
 
663
        }
 
664
        if (o) {
 
665
          if (PyList_Append(value, o) == -1)
 
666
            IGRAPH_ERROR("can't extend an edge attribute hash member", IGRAPH_FAILURE);
 
667
          else Py_DECREF(o);
 
668
        }
 
669
      }
 
670
    } else {
 
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);
 
674
        }
 
675
      }
 
676
    }
 
677
  }
 
678
  
 
679
  /*pos=0;
 
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");
 
683
  }*/
 
684
  
 
685
  /* Okay, now we added the new attribute values for the already existing
 
686
   * attribute keys. Let's see if we have something left */
 
687
  if (attr) {
 
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];
 
694
 
 
695
      value=PyList_New(j+ne);
 
696
      if (!value) {
 
697
        IGRAPH_ERROR("can't add attributes", IGRAPH_ENOMEM);
 
698
      }
 
699
 
 
700
      for (i=0; i<j; i++) {
 
701
        Py_INCREF(Py_None);
 
702
        PyList_SET_ITEM(value, i, Py_None);
 
703
      }
 
704
 
 
705
      for (i=0; i<ne; i++) {
 
706
        char *s;
 
707
        PyObject *o;
 
708
        switch (attr_rec->type) {
 
709
        case IGRAPH_ATTRIBUTE_NUMERIC:
 
710
          o=PyFloat_FromDouble((double)VECTOR(*(igraph_vector_t*)attr_rec->value)[i]);
 
711
          break;
 
712
        case IGRAPH_ATTRIBUTE_STRING:
 
713
          igraph_strvector_get((igraph_strvector_t*)attr_rec->value, i, &s);
 
714
          o=PyString_FromString(s);
 
715
          break;
 
716
        default:
 
717
          IGRAPH_WARNING("unsupported attribute type (not string and not numeric)");
 
718
          o=0;
 
719
          break;
 
720
        }
 
721
        if (o) PyList_SET_ITEM(value, i+j, o);
 
722
      }
 
723
 
 
724
      PyDict_SetItemString(dict, attr_rec->name, value);
 
725
      Py_DECREF(value);   /* compensate for PyDict_SetItemString */
 
726
    }
 
727
    free(added_attrs);
 
728
    IGRAPH_FINALLY_CLEAN(1);
 
729
  }
 
730
 
 
731
  return IGRAPH_SUCCESS;
 
732
}
 
733
 
 
734
/* Deleting edges */
 
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;
 
738
  Py_ssize_t pos=0;
 
739
  
 
740
  dict=((PyObject**)graph->attr)[2];
 
741
  if (!PyDict_Check(dict)) return;
 
742
 
 
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]) {
 
747
      ndeleted++;
 
748
      continue;
 
749
    }
 
750
 
 
751
    pos=0;
 
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);
 
757
      if (!o) {
 
758
        /* IndexError is already set, clear it and return */
 
759
        PyErr_Clear();
 
760
        return;
 
761
      }
 
762
      Py_INCREF(o);
 
763
      PyList_SetItem(value, VECTOR(*idx)[i]-1, o);
 
764
    }
 
765
  }
 
766
  /*printf("\n");*/
 
767
  
 
768
  /* Clear the remaining parts of the lists that aren't needed anymore */
 
769
  pos=0;
 
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");*/
 
775
  }
 
776
  
 
777
  return;
 
778
}
 
779
 
 
780
/* Permuting edges */
 
781
static int igraphmodule_i_attribute_permute_edges(igraph_t *graph,
 
782
                                                  const igraph_vector_t *idx) { 
 
783
  long int n, i;
 
784
  PyObject *key, *value, *dict, *newdict, *newlist, *o;
 
785
  Py_ssize_t pos=0;
 
786
 
 
787
  dict=((PyObject**)graph->attr)[2];
 
788
  if (!PyDict_Check(dict)) return 1;
 
789
 
 
790
  newdict=PyDict_New();
 
791
  if (!newdict) return 1;
 
792
 
 
793
  n=igraph_vector_size(idx);
 
794
  pos=0;
 
795
 
 
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);
 
800
      if (!o) {
 
801
        PyErr_Clear();
 
802
        return 1;
 
803
      }
 
804
      Py_INCREF(o);
 
805
      PyList_SET_ITEM(newlist, i, o);
 
806
    }
 
807
    PyDict_SetItem(newdict, key, newlist);
 
808
    Py_DECREF(newlist);
 
809
  }
 
810
 
 
811
  ((PyObject**)graph->attr)[2]=newdict;
 
812
  Py_DECREF(dict);
 
813
 
 
814
  return 0;
 
815
}
 
816
 
 
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;
 
828
  
 
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];
 
833
    PyObject *keys;
 
834
    PyObject *values;
 
835
    PyObject *o=0;
 
836
    keys=PyDict_Keys(dict);
 
837
    if (!keys) IGRAPH_ERROR("Internal error in PyDict_Keys", IGRAPH_FAILURE);
 
838
 
 
839
    if (n) {
 
840
      j=igraphmodule_PyList_to_strvector_t(keys, n);
 
841
      if (j) return j;
 
842
    }
 
843
    if (t) {
 
844
      k=PyList_Size(keys);
 
845
      igraph_vector_init(t, k);
 
846
      for (j=0; j<k; j++) {
 
847
        int is_numeric = 1; 
 
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;
 
854
          }
 
855
        } else if (o != Py_None && !PyNumber_Check(values)) is_numeric=0;
 
856
      
 
857
        VECTOR(*t)[j]=is_numeric ? IGRAPH_ATTRIBUTE_NUMERIC : IGRAPH_ATTRIBUTE_STRING;
 
858
      }
 
859
    }
 
860
    
 
861
    Py_DECREF(keys);
 
862
  }
 
863
 
 
864
  return 0;
 
865
}
 
866
 
 
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,
 
870
                                                const char* name) {
 
871
  long int attrnum;
 
872
  PyObject *o, *dict;
 
873
  switch (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;
 
878
  }
 
879
  dict = ((PyObject**)graph->attr)[attrnum];
 
880
  o = PyDict_GetItemString(dict, name);
 
881
  return o != 0;
 
882
}
 
883
 
 
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,
 
888
                                      const char *name) {
 
889
  long int attrnum, i, j;
 
890
  int is_numeric, is_string;
 
891
  PyObject *o, *dict;
 
892
 
 
893
  switch (elemtype) {
 
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;
 
898
  }
 
899
 
 
900
  /* Get the attribute dict */
 
901
  dict = ((PyObject**)graph->attr)[attrnum];
 
902
 
 
903
  /* Check whether the attribute exists */
 
904
  o = PyDict_GetItemString(dict, name);
 
905
  if (o == 0) IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL);
 
906
 
 
907
  /* Basic type check */
 
908
  if (!PyList_Check(o)) IGRAPH_ERROR("attribute hash type mismatch", IGRAPH_EINVAL);
 
909
  j = PyList_Size(o);
 
910
  if (j == 0) {
 
911
    *type = IGRAPH_ATTRIBUTE_NUMERIC;
 
912
    return 0;
 
913
  }
 
914
 
 
915
  /* Go on with the checks */
 
916
  is_numeric = is_string = 1;
 
917
  if (attrnum>0) {
 
918
 
 
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;
 
922
    }
 
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))
 
926
        is_string=0;
 
927
    }
 
928
  } else {
 
929
    if (o != Py_None && !PyNumber_Check(o)) is_numeric=0;
 
930
    if (o != Py_None && !PyString_Check(o) && !PyUnicode_Check(o))
 
931
      is_string=0;
 
932
  }
 
933
  if (is_numeric)
 
934
    *type = IGRAPH_ATTRIBUTE_NUMERIC;
 
935
  else if (is_string)
 
936
    *type = IGRAPH_ATTRIBUTE_STRING;
 
937
  else
 
938
    *type = IGRAPH_ATTRIBUTE_PY_OBJECT;
 
939
  return 0;
 
940
}
 
941
 
 
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));
 
952
  if (o == Py_None) {
 
953
    VECTOR(*value)[0] = IGRAPH_NAN;
 
954
    return 0;
 
955
  }
 
956
  result = PyNumber_Float(o);
 
957
  if (result) {
 
958
    VECTOR(*value)[0] = PyFloat_AsDouble(o);
 
959
    Py_DECREF(result);
 
960
  } else IGRAPH_ERROR("Internal error in PyFloat_AsDouble", IGRAPH_EINVAL); 
 
961
 
 
962
  return 0;
 
963
}
 
964
 
 
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");
 
975
  } else {
 
976
    result = PyObject_Str(o);
 
977
  }
 
978
  if (result) {
 
979
    IGRAPH_CHECK(igraph_strvector_set(value, 0, PyString_AsString(result)));
 
980
    Py_DECREF(result);
 
981
  } else IGRAPH_ERROR("Internal error in PyObject_Str", IGRAPH_EINVAL); 
 
982
 
 
983
  return 0;
 
984
}
 
985
 
 
986
/* Getting numeric vertex attributes */
 
987
int igraphmodule_i_get_numeric_vertex_attr(const igraph_t *graph,
 
988
                                           const char *name,
 
989
                                           igraph_vs_t vs,
 
990
                                           igraph_vector_t *value) {
 
991
  PyObject *dict, *list, *result, *o;
 
992
  igraph_vector_t newvalue;
 
993
 
 
994
  dict = ((PyObject**)graph->attr)[1];
 
995
  list = PyDict_GetItemString(dict, name);
 
996
  if (!list) IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL);
 
997
 
 
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);
 
1003
  } else {
 
1004
    igraph_vit_t it;
 
1005
    long int i=0;
 
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);
 
1012
      if (o != Py_None) {
 
1013
        result = PyNumber_Float(o);
 
1014
        VECTOR(*value)[i] = PyFloat_AsDouble(result);
 
1015
        Py_XDECREF(result);
 
1016
      } else VECTOR(*value)[i] = IGRAPH_NAN;
 
1017
      IGRAPH_VIT_NEXT(it);
 
1018
      i++;
 
1019
    }
 
1020
    igraph_vit_destroy(&it);
 
1021
    IGRAPH_FINALLY_CLEAN(1);
 
1022
  }
 
1023
 
 
1024
  return 0;
 
1025
}
 
1026
 
 
1027
/* Getting string vertex attributes */
 
1028
int igraphmodule_i_get_string_vertex_attr(const igraph_t *graph,
 
1029
                                          const char *name,
 
1030
                                          igraph_vs_t vs,
 
1031
                                          igraph_strvector_t *value) {
 
1032
  PyObject *dict, *list, *result;
 
1033
  igraph_strvector_t newvalue;
 
1034
 
 
1035
  dict = ((PyObject**)graph->attr)[1];
 
1036
  list = PyDict_GetItemString(dict, name);
 
1037
  if (!list) IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL);
 
1038
 
 
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);
 
1043
    *value=newvalue;
 
1044
  } else {
 
1045
    igraph_vit_t it;
 
1046
    long int i=0;
 
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");
 
1055
      } else {
 
1056
        result = PyObject_Str(result);
 
1057
      }
 
1058
      if (result == 0) {
 
1059
        IGRAPH_ERROR("Internal error in PyObject_Str", IGRAPH_EINVAL);
 
1060
      }
 
1061
      igraph_strvector_set(value, i, PyString_AsString(result));
 
1062
      Py_XDECREF(result);
 
1063
      IGRAPH_VIT_NEXT(it);
 
1064
      i++;
 
1065
    }
 
1066
    igraph_vit_destroy(&it);
 
1067
    IGRAPH_FINALLY_CLEAN(1);
 
1068
  }
 
1069
 
 
1070
  return 0;
 
1071
}
 
1072
 
 
1073
/* Getting numeric edge attributes */
 
1074
int igraphmodule_i_get_numeric_edge_attr(const igraph_t *graph,
 
1075
                                         const char *name,
 
1076
                                         igraph_es_t es,
 
1077
                                         igraph_vector_t *value) {
 
1078
  PyObject *dict, *list, *result, *o;
 
1079
  igraph_vector_t newvalue;
 
1080
 
 
1081
  dict = ((PyObject**)graph->attr)[2];
 
1082
  list = PyDict_GetItemString(dict, name);
 
1083
  if (!list) IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL);
 
1084
 
 
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);
 
1090
  } else {
 
1091
    igraph_eit_t it;
 
1092
    long int i=0;
 
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);
 
1099
      if (o != Py_None) {
 
1100
        result = PyNumber_Float(o);
 
1101
        VECTOR(*value)[i] = PyFloat_AsDouble(result);
 
1102
        Py_XDECREF(result);
 
1103
      } else VECTOR(*value)[i] = IGRAPH_NAN;
 
1104
      IGRAPH_EIT_NEXT(it);
 
1105
      i++;
 
1106
    }
 
1107
    igraph_eit_destroy(&it);
 
1108
    IGRAPH_FINALLY_CLEAN(1);
 
1109
  }
 
1110
 
 
1111
  return 0;
 
1112
}
 
1113
 
 
1114
/* Getting string edge attributes */
 
1115
int igraphmodule_i_get_string_edge_attr(const igraph_t *graph,
 
1116
                                        const char *name,
 
1117
                                        igraph_es_t es,
 
1118
                                        igraph_strvector_t *value) {
 
1119
  PyObject *dict, *list, *result;
 
1120
  igraph_strvector_t newvalue;
 
1121
 
 
1122
  dict = ((PyObject**)graph->attr)[2];
 
1123
  list = PyDict_GetItemString(dict, name);
 
1124
  if (!list) IGRAPH_ERROR("No such attribute", IGRAPH_EINVAL);
 
1125
 
 
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);
 
1130
    *value=newvalue;
 
1131
  } else {
 
1132
    igraph_eit_t it;
 
1133
    long int i=0;
 
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");
 
1142
      } else {
 
1143
        result = PyObject_Str(result);
 
1144
      }
 
1145
      if (result == 0) {
 
1146
        IGRAPH_ERROR("Internal error in PyObject_Str", IGRAPH_EINVAL);
 
1147
      }
 
1148
      igraph_strvector_set(value, i, PyString_AsString(result));
 
1149
      Py_XDECREF(result);
 
1150
      IGRAPH_EIT_NEXT(it);
 
1151
      i++;
 
1152
    }
 
1153
    igraph_eit_destroy(&it);
 
1154
    IGRAPH_FINALLY_CLEAN(1);
 
1155
  }
 
1156
 
 
1157
  return 0;
 
1158
}
 
1159
 
 
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,
 
1178
};
 
1179
 
 
1180
/** \ingroup python_interface
 
1181
 * \brief Method table for the igraph Python module
 
1182
 */
 
1183
static PyMethodDef igraphmodule_methods[] = 
 
1184
{
 
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"
 
1188
  },
 
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"
 
1198
      "  parameter."
 
1199
  },
 
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"
 
1207
  },
 
1208
  {NULL, NULL, 0, NULL}
 
1209
};
 
1210
 
 
1211
#ifndef PyMODINIT_FUNC
 
1212
#define PyMODINIT_FUNC void
 
1213
#endif
 
1214
 
 
1215
extern PyObject* igraphmodule_InternalError;
 
1216
extern PyObject* igraphmodule_arpack_options_default;
 
1217
 
 
1218
PyMODINIT_FUNC initcore(void) {
 
1219
  PyObject* m;
 
1220
  
 
1221
  if (PyType_Ready(&igraphmodule_VertexSeqType) < 0) return;
 
1222
  if (PyType_Ready(&igraphmodule_EdgeSeqType) < 0) return;
 
1223
  
 
1224
  igraphmodule_VertexType.tp_clear = (inquiry)igraphmodule_Vertex_clear;
 
1225
  if (PyType_Ready(&igraphmodule_VertexType) < 0) return;
 
1226
  
 
1227
  igraphmodule_EdgeType.tp_clear = (inquiry)igraphmodule_Edge_clear;
 
1228
  if (PyType_Ready(&igraphmodule_EdgeType) < 0) return;
 
1229
  
 
1230
  if (PyType_Ready(&igraphmodule_GraphType) < 0) return;
 
1231
  if (PyType_Ready(&igraphmodule_BFSIterType) < 0) return;
 
1232
  if (PyType_Ready(&igraphmodule_ARPACKOptionsType) < 0) return;
 
1233
 
 
1234
  m = Py_InitModule3("igraph.core", igraphmodule_methods,
 
1235
                     "Low-level Python interface for the igraph library. "
 
1236
                     "Should not be used directly.");
 
1237
  
 
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);
 
1245
 
 
1246
  /* Internal error exception type */
 
1247
  igraphmodule_InternalError =
 
1248
    PyErr_NewException("igraph.core.InternalError", PyExc_Exception, NULL);
 
1249
  PyModule_AddObject(m, "InternalError", igraphmodule_InternalError);
 
1250
 
 
1251
  /* ARPACK default options variable */
 
1252
  igraphmodule_arpack_options_default = igraphmodule_ARPACKOptions_new();
 
1253
  PyModule_AddObject(m, "arpack_options", igraphmodule_arpack_options_default);
 
1254
 
 
1255
  /* Useful constants */
 
1256
  PyModule_AddIntConstant(m, "OUT", IGRAPH_OUT);
 
1257
  PyModule_AddIntConstant(m, "IN", IGRAPH_IN);
 
1258
  PyModule_AddIntConstant(m, "ALL", IGRAPH_ALL);
 
1259
 
 
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);
 
1263
 
 
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);
 
1267
 
 
1268
  PyModule_AddIntConstant(m, "STRONG", IGRAPH_STRONG);
 
1269
  PyModule_AddIntConstant(m, "WEAK", IGRAPH_WEAK);
 
1270
 
 
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);
 
1274
 
 
1275
  PyModule_AddIntConstant(m, "REWIRING_SIMPLE", IGRAPH_REWIRING_SIMPLE);
 
1276
 
 
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);
 
1284
 
 
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);
 
1291
 
 
1292
  /* More useful constants */
 
1293
  PyModule_AddStringConstant(m, "__version__", "0.5.4");
 
1294
  PyModule_AddStringConstant(m, "__build_date__", __DATE__);
 
1295
 
 
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);
 
1301
  
 
1302
  /* initialize attribute handlers */
 
1303
  igraph_i_set_attribute_table(&igraphmodule_i_attribute_table);
 
1304
}