igraph-help
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [igraph] Use C to manipulate Python iGraph edge attributes.


From: Tamas Nepusz
Subject: Re: [igraph] Use C to manipulate Python iGraph edge attributes.
Date: Wed, 22 Jul 2015 22:48:00 +0200
User-agent: Mutt/1.5.23 (2014-03-12)

Dear Chris,

> I would like to use this code on graphs created in Python via a C extension
> but it looks to me like attributes for python graphs are stored in python and
> not in the underlying C data type that represents the graph.
Well, yes and no :) The C data type only has a void* pointer to an attribute
storage area and it is up to the attribute handler to decide how the attributes
are stored in that area. When you use igraph from C, you have to attach the
C-specific attribute handler to igraph to enable it to store attributes. When
you use igraph from Python, the igraph extension module attaches a different
attribute handler that enables arbitrary Python objects to be stored as
attributes -- but of course this is not compatible with the way the C attribute
handler stores them.

> Am I mistaken (and there is a way to manipulate the attributes directly from
> C)
If you are extending Python with a C module and has a pointer to a PyObject*
that actually stores an igraph graph, you can invoke its __graph_as_capsule,
__graph_as_cobject or _raw_pointer methods to gain access to the igraph_t
object that the PyObject* wraps. (__graph_as_capsule is available in Python
3.x, __graph_as_cobject is available in Python 2.x, _raw_pointer is available
in both -- the difference is only in their return types). You can use
PyObject_CallMethod() or PyObject_CallMethodObjArgs() from the C side to call
an arbitrary method of the Graph object (in Python).

Once you have the pointer to the igraph_t object wrapped by the Graph object,
you can do this to access the dictionaries that store the graph, vertex and
edge attributes:

typedef struct {
  PyObject* attrs[3];
  PyObject* vertex_name_index;
} igraph_attribute_storage;

#define ATTR_STORAGE(graph) ((igraph_attribute_storage*)((graph)->attr))->attrs
#define GRAPH_ATTR_DICT(graph) (ATTR_STORAGE(graph)[0])
#define VERTEX_ATTR_DICT(graph) (ATTR_STORAGE(graph)[1])
#define EDGE_ATTR_DICT(graph) (ATTR_STORAGE(graph)[2])

These are ordinary Python dictionaries so you can manipulate them from C as you
would do with any other Python dict. The keys are the names of the attributes
and the values are the attribute values (make sure that each value in the
vertex and edge dict is a list of length graph.vcount() and graph.ecount(),
respectively).

However, the above method is quite fragile as it relies directly on the
internal details of the attribute storage, and these are not considered part of
the public API of the Python interface of igraph. A more reliable way would be
to construct a Python list on the C side (using the PyList_* methods),
and pass it back to some Python code that does this:

graph.es["tie_range"] = the_list_that_you_returned

If you really want to push down the entire operation into the C layer,
including the above one-liner, you can:

1) Call PyObject_GetAttr() on your PyObject* that represents the graph to
   obtain its "es" attribute -- this way you effectively obtain graph.es

2) Call PyObject_SetItem() on graph.es with "tie_range" as the key and the list
   you have constructed as the value.

Make sure that you manage the reference counts appropriately if you decide to
go this way; e.g., PyObject_GetAttr() will return a new reference to graph.es
so you have to Py_DECREF() when you are done with it. PyObject_SetItem will
increase the reference count of the list you have constructed so you'll also
have to Py_DECREF() your list afterwards.

All the best,
T.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]