Commit 0bd803e9 authored by Tiago Peixoto's avatar Tiago Peixoto
Browse files

Implement Graph.purge_vertices() via graph copying in O(V + E) time

parent 6f91e1ac
...@@ -404,15 +404,12 @@ class PropertyMap(object): ...@@ -404,15 +404,12 @@ class PropertyMap(object):
def __register_map(self): def __register_map(self):
for g in [self.__g(), self.__base_g()]: for g in [self.__g(), self.__base_g()]:
if g is not None: if g is not None:
g._Graph__known_properties.append((self.key_type(), g._Graph__known_properties[id(self)] = weakref.ref(self)
weakref.ref(self.__map)))
def __unregister_map(self): def __unregister_map(self):
for g in [self.__g(), self.__base_g()]: for g in [self.__g(), self.__base_g()]:
if g is not None: if g is not None:
i = g._Graph__known_properties.index((self.key_type(), del g._Graph__known_properties[id(self)]
weakref.ref(self.__map)))
del g._Graph__known_properties[i]
def __del__(self): def __del__(self):
self.__unregister_map() self.__unregister_map()
...@@ -722,7 +719,7 @@ def group_vector_property(props, value_type=None, vprop=None, pos=None): ...@@ -722,7 +719,7 @@ def group_vector_property(props, value_type=None, vprop=None, pos=None):
def ungroup_vector_property(vprop, pos, props=None): def ungroup_vector_property(vprop, pos, props=None):
"""Ungroup vector property map ``vprop`` into a list of individual property maps. """Ungroup vector property map ``vprop`` into a list of individual property maps.
Parameters Parameters
---------- ----------
vprop : :class:`~graph_tool.PropertyMap` vprop : :class:`~graph_tool.PropertyMap`
Vector property map to be ungrouped. Vector property map to be ungrouped.
...@@ -931,10 +928,11 @@ class Graph(object): ...@@ -931,10 +928,11 @@ class Graph(object):
If ``g`` is specified, the graph (and its internal properties) will be If ``g`` is specified, the graph (and its internal properties) will be
copied. copied.
If ``prune`` is set to True, and ``g`` is specified, only the filtered graph If ``prune`` is set to ``True``, and ``g`` is specified, only the filtered
will be copied, and the new graph object will not be filtered. Optionally, a graph will be copied, and the new graph object will not be
tuple of three booleans can be passed as value to ``prune``, to specify a filtered. Optionally, a tuple of three booleans can be passed as value to
different behavior to vertex, edge, and reversal filters, respectively. ``prune``, to specify a different behavior to vertex, edge, and reversal
filters, respectively.
The graph is implemented as an `adjacency list`_, where both vertex and edge The graph is implemented as an `adjacency list`_, where both vertex and edge
lists are C++ STL vectors. lists are C++ STL vectors.
...@@ -945,7 +943,7 @@ class Graph(object): ...@@ -945,7 +943,7 @@ class Graph(object):
def __init__(self, g=None, directed=True, prune=False): def __init__(self, g=None, directed=True, prune=False):
self.__properties = {} self.__properties = {}
self.__known_properties = [] self.__known_properties = {}
self.__filter_state = {"reversed": False, self.__filter_state = {"reversed": False,
"edge_filter": (None, False), "edge_filter": (None, False),
"vertex_filter": (None, False), "vertex_filter": (None, False),
...@@ -1159,9 +1157,9 @@ class Graph(object): ...@@ -1159,9 +1157,9 @@ class Graph(object):
self.__check_perms("del_vertex") self.__check_perms("del_vertex")
vertex = self.vertex(int(vertex)) vertex = self.vertex(int(vertex))
index = self.vertex_index[vertex] index = self.vertex_index[vertex]
for pmap in self.__known_properties: for pmap in self.__known_properties.values():
if pmap[0] == "v" and pmap[1]() != None and pmap[1]().is_writable(): if pmap() is not None and pmap().key_type() == "v" and pmap().is_writable():
self.__graph.ShiftVertexProperty(pmap[1]().get_map(), index) self.__graph.ShiftVertexProperty(pmap()._PropertyMap__map.get_map(), index)
self.clear_vertex(vertex) self.clear_vertex(vertex)
libcore.remove_vertex(self.__graph, vertex) libcore.remove_vertex(self.__graph, vertex)
...@@ -1639,17 +1637,51 @@ class Graph(object): ...@@ -1639,17 +1637,51 @@ class Graph(object):
indicating whether or not it is inverted.""" indicating whether or not it is inverted."""
return self.__filter_state["edge_filter"] return self.__filter_state["edge_filter"]
def purge_vertices(self): def purge_vertices(self, in_place=False):
"""Remove all vertices of the graph which are currently being filtered """Remove all vertices of the graph which are currently being filtered
out, and return it to the unfiltered state. This operation is not out, and return it to the unfiltered state. This operation is not
reversible.""" reversible.
old_indexes = self.vertex_index.copy("int32_t")
self.__graph.PurgeVertices(_prop("v", self, old_indexes)) If the option ``in_place == True`` is given, the algorithm will remove
self.set_vertex_filter(None) the filtered vertices and re-index all property maps which are tied with
for pmap in self.__known_properties: the graph. This is a slow operation which has an :math:`O(N^2)`
if pmap[0] == "v" and pmap[1]() != None and pmap[1]().is_writable(): complexity.
self.__graph.ReIndexVertexProperty(pmap[1]().get_map(),
_prop("v", self, old_indexes)) If ``in_place == False``, the graph and its vertex and edge property
maps are temporarily copied to a new unfiltered graph, which will
replace the contents of the original graph. This is a fast operation
with an :math:`O(N + E)` complexity. This is the default behaviour if no
option is given.
"""
if in_place:
old_indexes = self.vertex_index.copy("int32_t")
self.__graph.PurgeVertices(_prop("v", self, old_indexes))
self.set_vertex_filter(None)
for pmap in self.__known_properties.values():
if (pmap() is not None and pmap().key_type() == "v" and
pmap() not in [self.vertex_index, self.edge_index]):
self.__graph.ReIndexVertexProperty(pmap()._PropertyMap__map.get_map(),
_prop("v", self, old_indexes))
else:
stamp = id(self)
pmaps = []
for pmap in self.__known_properties.values():
if (pmap() is not None and pmap().key_type() in ["v", "e"] and
pmap() not in [self.vertex_index, self.edge_index]):
pmaps.append(pmap())
pname = "__tmp_purge_vertices_%d_%d" % (stamp, id(pmaps[-1]))
self.properties[(pmaps[-1].key_type(), pname)] = pmaps[-1]
new_g = Graph(self, prune=(True, False, False))
self.__graph = new_g.__graph
self.set_vertex_filter(None)
for pmap in pmaps:
pname = "__tmp_purge_vertices_%d_%d" % (stamp, id(pmap))
new_pmap = new_g.properties[(pmap.key_type(), pname)]
pmap._PropertyMap__map = new_pmap._PropertyMap__map
del self.properties[(pmap.key_type(), pname)]
def purge_edges(self): def purge_edges(self):
"""Remove all edges of the graph which are currently being filtered out, """Remove all edges of the graph which are currently being filtered out,
......
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