Commit e5a1861d authored by Tiago Peixoto's avatar Tiago Peixoto

Fix bug with remove_vertex() and include 'fast' option

This fixes ticket #131, and includes a 'fast' O(k) option, which
changes the vertex index ordering.
parent e9cbea5f
......@@ -100,6 +100,7 @@ public:
void Clear();
void ClearEdges();
void ShiftVertexProperty(boost::any map, size_t index) const;
void MoveVertexProperty(boost::any map, size_t index) const;
void ReIndexVertexProperty(boost::any map, boost::any old_index) const;
void CopyVertexProperty(const GraphInterface& src, boost::any prop_src,
boost::any prop_tgt);
......
......@@ -96,6 +96,9 @@ void clear_vertex(Vertex v, adj_list<Vertex>& g);
template <class Vertex>
void remove_vertex(Vertex v, adj_list<Vertex>& g);
template <class Vertex>
void remove_vertex_fast(Vertex v, adj_list<Vertex>& g);
template <class Vertex>
std::pair<typename adj_list<Vertex>::edge_descriptor, bool>
add_edge(Vertex s, Vertex t, adj_list<Vertex>& g);
......@@ -294,6 +297,8 @@ private:
friend void remove_vertex<>(Vertex v, adj_list<Vertex>& g);
friend void remove_vertex_fast<>(Vertex v, adj_list<Vertex>& g);
friend std::pair<edge_descriptor, bool>
add_edge<>(Vertex s, Vertex t, adj_list<Vertex>& g);
......@@ -525,14 +530,77 @@ inline void clear_vertex(Vertex v, adj_list<Vertex>& g)
ies.clear();
}
// O(V + E)
template <class Vertex>
inline void remove_vertex(Vertex v, adj_list<Vertex>& g)
{
clear_vertex(v, g);
g._out_edges.erase(g._out_edges.begin() + v);
g._in_edges.erase(g._in_edges.begin() + v);
int i, N = g._out_edges.size();
#pragma omp parallel for default(shared) private(i)
for (i = 0; i < N; ++i)
{
for (size_t j = 0; j < g._out_edges[i].size(); ++j)
{
if (g._out_edges[i][j].first > v)
g._out_edges[i][j].first--;
}
for (size_t j = 0; j < g._in_edges[i].size(); ++j)
{
if (g._in_edges[i][j].first > v)
g._in_edges[i][j].first--;
}
}
}
// O(k + k_last)
template <class Vertex>
inline void remove_vertex_fast(Vertex v, adj_list<Vertex>& g)
{
clear_vertex(v, g);
Vertex back = g._out_edges.size() - 1;
if (v < back)
{
g._out_edges[v].swap(g._out_edges[back]);
g._in_edges[v].swap(g._in_edges[back]);
for (size_t i = 0; i < g._out_edges[v].size(); ++i)
{
Vertex u = g._out_edges[v][i].first;
if (u == back)
{
g._out_edges[v][i].first = v;
}
else
{
for (size_t j = 0; j < g._in_edges[u].size(); ++j)
if (g._in_edges[u][j].first == back)
g._in_edges[u][j].first = v;
}
}
for (size_t i = 0; i < g._in_edges[v].size(); ++i)
{
Vertex u = g._in_edges[v][i].first;
if (u == back)
{
g._out_edges[v][i].first = v;
}
else
{
for (size_t j = 0; j < g._out_edges[u].size(); ++j)
if (g._out_edges[u][j].first == back)
g._out_edges[u][j].first = v;
}
}
}
g._out_edges.pop_back();
g._in_edges.pop_back();
}
template <class Vertex>
inline typename std::pair<typename adj_list<Vertex>::edge_descriptor, bool>
......
......@@ -382,6 +382,7 @@ BOOST_PYTHON_MODULE(libgraph_tool_core)
.def("PurgeVertices", &GraphInterface::PurgeVertices)
.def("PurgeEdges", &GraphInterface::PurgeEdges)
.def("ShiftVertexProperty", &GraphInterface::ShiftVertexProperty)
.def("MoveVertexProperty", &GraphInterface::MoveVertexProperty)
.def("ReIndexVertexProperty", &GraphInterface::ReIndexVertexProperty)
.def("WriteToFile", &GraphInterface::WriteToFile)
.def("ReadFromFile",&GraphInterface::ReadFromFile)
......
......@@ -72,6 +72,35 @@ void GraphInterface::ShiftVertexProperty(boost::any prop, size_t index) const
throw GraphException("invalid writable property map");
}
struct move_vertex_property
{
template <class PropertyMap>
void operator()(PropertyMap, const GraphInterface::multigraph_t& g,
boost::any map, size_t vi, size_t back, bool& found) const
{
try
{
PropertyMap pmap = any_cast<PropertyMap>(map);
pmap[vertex(vi,g)] = pmap[vertex(back,g)];
found = true;
}
catch (bad_any_cast&) {}
}
};
// this function will move the back of the property map when a vertex in the middle is to be deleted
void GraphInterface::MoveVertexProperty(boost::any prop, size_t index) const
{
size_t back = num_vertices(*_mg) - 1;
bool found = false;
mpl::for_each<writable_vertex_properties>
(bind<void>(move_vertex_property(), _1, ref(*_mg),
prop, index, back, ref(found)));
if (!found)
throw GraphException("invalid writable property map");
}
struct reindex_vertex_property
{
template <class PropertyMap, class IndexMap>
......
......@@ -155,16 +155,17 @@ struct shift_vertex_property
}
};
void remove_vertex(GraphInterface& gi, const python::object& v)
void remove_vertex(GraphInterface& gi, const python::object& v, bool fast)
{
PythonVertex& pv = python::extract<PythonVertex&>(v);
pv.CheckValid();
GraphInterface::vertex_t dv = pv.GetDescriptor();
pv.SetValid(false);
//remove vertex
clear_vertex(dv, gi.GetGraph());
remove_vertex(dv, gi.GetGraph());
if (fast)
remove_vertex_fast(dv, gi.GetGraph());
else
remove_vertex(dv, gi.GetGraph());
}
struct add_new_edge
......
......@@ -1152,16 +1152,44 @@ class Graph(object):
pos = self.num_vertices() - n
return (self.vertex(i) for i in range(pos, pos + n))
def remove_vertex(self, vertex):
"""Remove a vertex from the graph."""
def remove_vertex(self, vertex, fast=False):
r"""Remove a vertex from the graph.
.. note::
If the option ``fast == False`` is given, this operation is
:math:`O(N + E)` (this is the default). Otherwise it is
:math:`O(k + k_{\text{last}})`, where :math:`k` is the (total)
degree of the vertex being deleted, and :math:`k_{\text{last}}` is
the (total) degree of the vertex with the largest index.
.. warning::
If ``fast == True``, the vertex being deleted is 'swapped' with the
last vertex (i.e. with the largest index), which will in turn inherit
the index of the vertex being deleted. All property maps associated
with the graph will be properly updated, but the index ordering of
the graph will no longer be the same.
"""
self.__check_perms("del_vertex")
vertex = self.vertex(int(vertex))
index = self.vertex_index[vertex]
for pmap in self.__known_properties.values():
if pmap() is not None and pmap().key_type() == "v" and pmap().is_writable():
self.__graph.ShiftVertexProperty(pmap()._PropertyMap__map.get_map(), index)
self.clear_vertex(vertex)
libcore.remove_vertex(self.__graph, vertex)
self.stash_filter(vertex=True)
back = self.num_vertices() - 1
self.pop_filter(vertex=True)
# move / shift all known property maps
if index != back:
for pmap in self.__known_properties.values():
if pmap() is not None and pmap().key_type() == "v" and pmap().is_writable():
if fast:
self.__graph.MoveVertexProperty(pmap()._PropertyMap__map.get_map(), index)
else:
self.__graph.ShiftVertexProperty(pmap()._PropertyMap__map.get_map(), index)
libcore.remove_vertex(self.__graph, vertex, fast)
def remove_vertex_if(self, predicate):
"""Remove all the vertices from the graph for which ``predicate(v)``
......
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