Commit a9fbbe1a authored by Tiago Peixoto's avatar Tiago Peixoto

Preserve reference to graph during vertex/edge iteration

This fixes issue #685.

This also exposes the non-descriptor based iteration in the documentation.
parent 326a176b
Pipeline #743 failed with stage
in 68 minutes and 12 seconds
...@@ -18,11 +18,24 @@ ...@@ -18,11 +18,24 @@
See :ref:`sec_iteration` for more documentation and examples. See :ref:`sec_iteration` for more documentation and examples.
Iterator-based interface: Iterator-based interface with descriptors:
.. automethod:: vertices .. automethod:: vertices
.. automethod:: edges .. automethod:: edges
Iterator-based interface without descriptors:
.. automethod:: iter_vertices
.. automethod:: iter_edges
.. automethod:: iter_out_edges
.. automethod:: iter_in_edges
.. automethod:: iter_all_edges
.. automethod:: iter_out_neighbors
.. automethod:: iter_in_neighbors
.. automethod:: iter_all_neighbors
Array-based interface: Array-based interface:
.. automethod:: get_vertices .. automethod:: get_vertices
......
...@@ -348,13 +348,76 @@ vertices in the graph. ...@@ -348,13 +348,76 @@ vertices in the graph.
is being used. Removal during iteration will cause bad things to is being used. Removal during iteration will cause bad things to
happen. happen.
Fast iteration over vertices and edges Faster iteration over vertices and edges without descriptor
"""""""""""""""""""""""""""""""""""""" """""""""""""""""""""""""""""""""""""""""""""""""""""""""""
The mode of iteration considered above is convenient, but requires the
creation of vertex and edge descriptor objects, which incurs a
performance overhead. A faster approach involves the use of the methods
:meth:`~graph_tool.Graph.iter_vertices`,
:meth:`~graph_tool.Graph.iter_edges`,
:meth:`~graph_tool.Graph.iter_out_edges`,
:meth:`~graph_tool.Graph.iter_in_edges`,
:meth:`~graph_tool.Graph.iter_all_edges`,
:meth:`~graph_tool.Graph.iter_out_neighbors`,
:meth:`~graph_tool.Graph.iter_in_neighbors`,
:meth:`~graph_tool.Graph.iter_all_neighbors`, which return vertex
indexes and pairs thereof, instead of descriptors objects, to specify
vertex and edges, respectively.
The equivalent of the above examples can be obtained as:
While convenient, looping over the graph as described in the previous .. testcode::
section is not the most efficient approach. This is because the loops
are performed in pure Python, and hence it undermines the main feature for v in g.iter_vertices():
of the library, which is the offloading of loops from Python to print(v)
for e in g.iter_edges():
print(e)
.. testoutput::
:hide:
0
1
2
3
4
5
6
7
8
9
10
11
(0, 1)
(2, 3)
and likewise for the iteration over the neighborhood of a vertex:
.. testcode::
for v in g.iter_vertices():
for e in g.iter_out_edges(v):
print(e)
for w in g.iter_out_neighbors(v):
print(w)
.. testoutput::
:hide:
(0, 1)
1
(2, 3)
3
Even faster iteration over vertices and edges using arrays
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
While more convenient, looping over the graph as described in the
previous sections are not the most efficient approaches. This is because
the loops are performed in pure Python, and hence it undermines the main
feature of the library, which is the offloading of loops from Python to
C++. Following the :mod:`numpy` philosophy, :mod:`graph_tool` also C++. Following the :mod:`numpy` philosophy, :mod:`graph_tool` also
provides an array-based interface that avoids loops in Python. This is provides an array-based interface that avoids loops in Python. This is
done with the :meth:`~graph_tool.Graph.get_vertices`, done with the :meth:`~graph_tool.Graph.get_vertices`,
......
...@@ -1787,7 +1787,9 @@ class Graph(object): ...@@ -1787,7 +1787,9 @@ class Graph(object):
>>> assert(vlist == vlist2) >>> assert(vlist == vlist2)
""" """
return libcore.get_vertices(self.__graph) viter = libcore.get_vertices(self.__graph)
viter._g = self
return viter
def iter_vertices(self, vprops=[]): def iter_vertices(self, vprops=[]):
"""Return an iterator over the vertex indices, and optional vertex properties """Return an iterator over the vertex indices, and optional vertex properties
...@@ -1814,8 +1816,10 @@ class Graph(object): ...@@ -1814,8 +1816,10 @@ class Graph(object):
5 5
""" """
return libcore.get_vertex_iter(self.__graph, 0, viter = libcore.get_vertex_iter(self.__graph, 0,
[vp._get_any() for vp in vprops]) [vp._get_any() for vp in vprops])
viter._g = self
return viter
def get_vertices(self, vprops=[]): def get_vertices(self, vprops=[]):
"""Return a :class:`numpy.ndarray` containing the vertex indices, and optional """Return a :class:`numpy.ndarray` containing the vertex indices, and optional
...@@ -1902,7 +1906,9 @@ class Graph(object): ...@@ -1902,7 +1906,9 @@ class Graph(object):
ordering. ordering.
""" """
return libcore.get_edges(self.__graph) eiter = libcore.get_edges(self.__graph)
eiter._g = self
return eiter
def iter_edges(self, eprops=[]): def iter_edges(self, eprops=[]):
"""Return an iterator over the edge ```(source, target)`` pairs, and optional """Return an iterator over the edge ```(source, target)`` pairs, and optional
...@@ -1916,16 +1922,19 @@ class Graph(object): ...@@ -1916,16 +1922,19 @@ class Graph(object):
Examples Examples
-------- --------
>>> g = gt.random_graph(6, lambda: 1, directed=False) >>> g = gt.collection.data["karate"]
>>> for s, t, i in g.iter_edges([g.edge_index]) >>> for s, t, i in g.iter_edges([g.edge_index]):
... print(s, t, i) ... print(s, t, i)
... if s == 5:
... break
2 1 2 2 1 2
3 4 0 3 4 0
5 0 1 5 0 1
""" """
return libcore.get_edge_iter(self.__graph, 0, eiter = libcore.get_edge_iter(self.__graph, 0,
[ep._get_any() for ep in eprops]) [ep._get_any() for ep in eprops])
eiter._g = self
return eiter
def get_edges(self, eprops=[]): def get_edges(self, eprops=[]):
"""Return a :class:`numpy.ndarray` containing the edges, and optional edge """Return a :class:`numpy.ndarray` containing the edges, and optional edge
...@@ -1964,15 +1973,18 @@ class Graph(object): ...@@ -1964,15 +1973,18 @@ class Graph(object):
Examples Examples
-------- --------
>>> g = gt.collection.data["pgp-strong-2009"] >>> g = gt.collection.data["pgp-strong-2009"]
>>> for s, t, i in g.iter_out_edges(66, [g.edge_index]) >>> for s, t, i in g.iter_out_edges(66, [g.edge_index]):
... print(s, t, i) ... print(s, t, i)
2 1 2 66 63 5266
3 4 0 66 20369 5267
5 0 1 66 13980 5268
66 8687 5269
66 38674 5270
""" """
return libcore.get_out_edge_iter(self.__graph, int(v), eiter = libcore.get_out_edge_iter(self.__graph, int(v),
[ep._get_any() for ep in eprops]) [ep._get_any() for ep in eprops])
eiter._g = self
return eiter
def get_out_edges(self, v, eprops=[]): def get_out_edges(self, v, eprops=[]):
"""Return a :class:`numpy.ndarray` containing the out-edges of vertex ``v``, and """Return a :class:`numpy.ndarray` containing the out-edges of vertex ``v``, and
...@@ -2009,15 +2021,16 @@ class Graph(object): ...@@ -2009,15 +2021,16 @@ class Graph(object):
Examples Examples
-------- --------
>>> g = gt.collection.data["pgp-strong-2009"] >>> g = gt.collection.data["pgp-strong-2009"]
>>> for s, t, i in g.iter_in_edges(66, [g.edge_index]) >>> for s, t, i in g.iter_in_edges(66, [g.edge_index]):
... print(s, t, i) ... print(s, t, i)
2 1 2 8687 66 179681
3 4 0 20369 66 255033
5 0 1 38674 66 300230
""" """
return libcore.get_out_edge_iter(self.__graph, int(v), eiter = libcore.get_in_edge_iter(self.__graph, int(v),
[ep._get_any() for ep in eprops]) [ep._get_any() for ep in eprops])
eiter._g = self
return eiter
def get_in_edges(self, v, eprops=[]): def get_in_edges(self, v, eprops=[]):
"""Return a :class:`numpy.ndarray` containing the in-edges of vertex ``v``, and """Return a :class:`numpy.ndarray` containing the in-edges of vertex ``v``, and
...@@ -2045,21 +2058,27 @@ class Graph(object): ...@@ -2045,21 +2058,27 @@ class Graph(object):
.. note:: .. note::
This mode of iteration is more efficient than using This mode of iteration is more efficient than using
:meth:`~graph_tool.Vertex.in_edges`, as descriptor objects are not :meth:`~graph_tool.Vertex.all_edges`, as descriptor objects are not
created. created.
Examples Examples
-------- --------
>>> g = gt.collection.data["pgp-strong-2009"] >>> g = gt.collection.data["pgp-strong-2009"]
>>> for s, t, i in g.iter_all_edges(66, [g.edge_index]) >>> for s, t, i in g.iter_all_edges(66, [g.edge_index]):
... print(s, t, i) ... print(s, t, i)
2 1 2 66 63 5266
3 4 0 66 20369 5267
5 0 1 66 13980 5268
66 8687 5269
66 38674 5270
8687 66 179681
20369 66 255033
38674 66 300230
""" """
return libcore.get_out_edge_iter(self.__graph, int(v), eiter = libcore.get_all_edge_iter(self.__graph, int(v),
[ep._get_any() for ep in eprops]) [ep._get_any() for ep in eprops])
eiter._g = self
return eiter
def get_all_edges(self, v, eprops=[]): def get_all_edges(self, v, eprops=[]):
"""Return a :class:`numpy.ndarray` containing the in- and out-edges of vertex v, """Return a :class:`numpy.ndarray` containing the in- and out-edges of vertex v,
...@@ -2099,15 +2118,18 @@ class Graph(object): ...@@ -2099,15 +2118,18 @@ class Graph(object):
Examples Examples
-------- --------
>>> g = gt.collection.data["pgp-strong-2009"] >>> g = gt.collection.data["pgp-strong-2009"]
>>> for s, t, i in g.iter_out_neighbors(66, [g.vp.uid]) >>> for u, i in g.iter_out_neighbors(66, [g.vp.uid]):
... print(s, t, i) ... print(u, i)
2 1 2 63 ['paul wilders <webmaster@wilders.org>']
3 4 0 20369 ['Zhen-Xjell <zhen-xjell@teamhelix.net>']
5 0 1 13980 ['Hooman <Hooman@iname.com>']
8687 ['H. Loeung (howe81) <howe81@unixque.com>', 'howe81 <howe81@bigpond.net.au>', 'Howie L (howe81) <howe81@bigpond.net.au>']
38674 ['Howie L (howe81) <howe81@bigpond.net.au>']
""" """
return libcore.get_out_neighbors_iter(self.__graph, int(v), viter = libcore.get_out_neighbors_iter(self.__graph, int(v),
[vp._get_any() for vp in vprops]) [vp._get_any() for vp in vprops])
viter._g = self
return viter
iter_out_neighbours = iter_out_neighbors iter_out_neighbours = iter_out_neighbors
...@@ -2148,15 +2170,16 @@ class Graph(object): ...@@ -2148,15 +2170,16 @@ class Graph(object):
Examples Examples
-------- --------
>>> g = gt.collection.data["pgp-strong-2009"] >>> g = gt.collection.data["pgp-strong-2009"]
>>> for s, t, i in g.iter_in_neighbors(66, [g.vp.uid]) >>> for u, i in g.iter_in_neighbors(66, [g.vp.uid]):
... print(s, t, i) ... print(u, i)
2 1 2 8687 ['H. Loeung (howe81) <howe81@unixque.com>', 'howe81 <howe81@bigpond.net.au>', 'Howie L (howe81) <howe81@bigpond.net.au>']
3 4 0 20369 ['Zhen-Xjell <zhen-xjell@teamhelix.net>']
5 0 1 38674 ['Howie L (howe81) <howe81@bigpond.net.au>']
""" """
return libcore.get_in_neighbors_iter(self.__graph, int(v), viter = libcore.get_in_neighbors_iter(self.__graph, int(v),
[vp._get_any() for vp in vprops]) [vp._get_any() for vp in vprops])
viter._g = self
return viter
iter_in_neighbours = iter_in_neighbors iter_in_neighbours = iter_in_neighbors
...@@ -2197,15 +2220,21 @@ class Graph(object): ...@@ -2197,15 +2220,21 @@ class Graph(object):
Examples Examples
-------- --------
>>> g = gt.collection.data["pgp-strong-2009"] >>> g = gt.collection.data["pgp-strong-2009"]
>>> for s, t, i in g.iter_all_neighbors(66, [g.vp.uid]) >>> for u, i in g.iter_all_neighbors(66, [g.vp.uid]):
... print(s, t, i) ... print(u, i)
2 1 2 63 ['paul wilders <webmaster@wilders.org>']
3 4 0 20369 ['Zhen-Xjell <zhen-xjell@teamhelix.net>']
5 0 1 13980 ['Hooman <Hooman@iname.com>']
8687 ['H. Loeung (howe81) <howe81@unixque.com>', 'howe81 <howe81@bigpond.net.au>', 'Howie L (howe81) <howe81@bigpond.net.au>']
38674 ['Howie L (howe81) <howe81@bigpond.net.au>']
8687 ['H. Loeung (howe81) <howe81@unixque.com>', 'howe81 <howe81@bigpond.net.au>', 'Howie L (howe81) <howe81@bigpond.net.au>']
20369 ['Zhen-Xjell <zhen-xjell@teamhelix.net>']
38674 ['Howie L (howe81) <howe81@bigpond.net.au>']
""" """
return libcore.get_in_neighbors_iter(self.__graph, int(v), viter = libcore.get_all_neighbors_iter(self.__graph, int(v),
[vp._get_any() for vp in vprops]) [vp._get_any() for vp in vprops])
viter._g = self
return viter
iter_all_neighbours = iter_all_neighbors iter_all_neighbours = iter_all_neighbors
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment