Commit 4296b96e authored by Tiago Peixoto's avatar Tiago Peixoto
Browse files

Implement optional faster O(1) removal of edges

parent e16c781f
...@@ -86,6 +86,9 @@ public: ...@@ -86,6 +86,9 @@ public:
bool GetDirected() {return _directed;} bool GetDirected() {return _directed;}
void SetReversed(bool reversed) {_reversed = reversed;} void SetReversed(bool reversed) {_reversed = reversed;}
bool GetReversed() {return _reversed;} bool GetReversed() {return _reversed;}
void SetKeepEpos(bool keep) {_mg->set_keep_epos(keep);}
bool GetKeepEpos() {return _mg->get_keep_epos();}
// graph filtering // graph filtering
void SetVertexFilterProperty(boost::any prop, bool invert); void SetVertexFilterProperty(boost::any prop, bool invert);
......
...@@ -250,6 +250,28 @@ public: ...@@ -250,6 +250,28 @@ public:
_last_idx)); _last_idx));
_last_idx++; _last_idx++;
} }
if (_keep_epos)
rebuild_epos();
}
void set_keep_epos(bool keep)
{
if (keep)
{
if(!_keep_epos)
rebuild_epos();
}
else
{
_epos.clear();
}
_keep_epos = keep;
}
bool get_keep_epos()
{
return _keep_epos;
} }
size_t get_last_index() const {return _last_idx;} size_t get_last_index() const {return _last_idx;}
...@@ -266,6 +288,27 @@ private: ...@@ -266,6 +288,27 @@ private:
// for new edges to avoid very large // for new edges to avoid very large
// indexes, and unnecessary property map // indexes, and unnecessary property map
// memory use // memory use
bool _keep_epos;
std::vector<std::pair<int32_t, int32_t> > _epos;
void rebuild_epos()
{
_epos.resize(_last_idx + 1);
for (size_t i = 0; i < _out_edges.size(); ++i)
{
for (size_t j = 0; j < _out_edges[i].size(); ++j)
{
size_t idx = _out_edges[i][j].second;
_epos[idx].first = j;
}
for (size_t j = 0; j < _in_edges[i].size(); ++j)
{
size_t idx = _in_edges[i][j].second;
_epos[idx].second = j;
}
}
}
// manipulation functions // manipulation functions
friend std::pair<vertex_iterator, vertex_iterator> friend std::pair<vertex_iterator, vertex_iterator>
...@@ -631,6 +674,14 @@ add_edge(Vertex s, Vertex t, adj_list<Vertex>& g) ...@@ -631,6 +674,14 @@ add_edge(Vertex s, Vertex t, adj_list<Vertex>& g)
g._in_edges[t].push_back(std::make_pair(s, idx)); g._in_edges[t].push_back(std::make_pair(s, idx));
g._n_edges++; g._n_edges++;
if (g._keep_epos)
{
if (idx >= g._epos.size())
g._epos.resize(idx + 1);
g._epos[idx].first = g._out_edges[s].size() - 1;
g._epos[idx].second = g._in_edges[t].size() - 1;
}
return std::make_pair(std::tr1::make_tuple(s, t, idx), true); return std::make_pair(std::tr1::make_tuple(s, t, idx), true);
} }
...@@ -665,6 +716,10 @@ inline void remove_edge(const typename adj_list<Vertex>::edge_descriptor& e, ...@@ -665,6 +716,10 @@ inline void remove_edge(const typename adj_list<Vertex>::edge_descriptor& e,
Vertex t = get<1>(e); Vertex t = get<1>(e);
Vertex idx = get<2>(e); Vertex idx = get<2>(e);
typename adj_list<Vertex>::edge_list_t& oes = g._out_edges[s]; typename adj_list<Vertex>::edge_list_t& oes = g._out_edges[s];
typename adj_list<Vertex>::edge_list_t& ies = g._in_edges[t];
if (!g._keep_epos) // O(k_s + k_t)
{
for (size_t i = 0; i < oes.size(); ++i) for (size_t i = 0; i < oes.size(); ++i)
{ {
if (t == oes[i].first && idx == oes[i].second) if (t == oes[i].first && idx == oes[i].second)
...@@ -674,7 +729,6 @@ inline void remove_edge(const typename adj_list<Vertex>::edge_descriptor& e, ...@@ -674,7 +729,6 @@ inline void remove_edge(const typename adj_list<Vertex>::edge_descriptor& e,
} }
} }
typename adj_list<Vertex>::edge_list_t& ies = g._in_edges[t];
for (size_t i = 0; i < ies.size(); ++i) for (size_t i = 0; i < ies.size(); ++i)
{ {
if (s == ies[i].first && idx == ies[i].second) if (s == ies[i].first && idx == ies[i].second)
...@@ -683,6 +737,19 @@ inline void remove_edge(const typename adj_list<Vertex>::edge_descriptor& e, ...@@ -683,6 +737,19 @@ inline void remove_edge(const typename adj_list<Vertex>::edge_descriptor& e,
break; break;
} }
} }
}
else // O(1)
{
const std::pair<int32_t, int32_t>& pos = g._epos[idx];
g._epos[oes.back().second].first = pos.first;
oes[pos.first] = oes.back();
oes.pop_back();
g._epos[ies.back().second].second = pos.second;
ies[pos.second] = ies.back();
ies.pop_back();
}
g._free_indexes.push_back(idx); g._free_indexes.push_back(idx);
g._n_edges--; g._n_edges--;
} }
......
...@@ -373,6 +373,8 @@ BOOST_PYTHON_MODULE(libgraph_tool_core) ...@@ -373,6 +373,8 @@ BOOST_PYTHON_MODULE(libgraph_tool_core)
.def("GetDirected", &GraphInterface::GetDirected) .def("GetDirected", &GraphInterface::GetDirected)
.def("SetReversed", &GraphInterface::SetReversed) .def("SetReversed", &GraphInterface::SetReversed)
.def("GetReversed", &GraphInterface::GetReversed) .def("GetReversed", &GraphInterface::GetReversed)
.def("SetKeepEpos", &GraphInterface::SetKeepEpos)
.def("GetKeepEpos", &GraphInterface::GetKeepEpos)
.def("SetVertexFilterProperty", .def("SetVertexFilterProperty",
&GraphInterface::SetVertexFilterProperty) &GraphInterface::SetVertexFilterProperty)
.def("IsVertexFilterActive", &GraphInterface::IsVertexFilterActive) .def("IsVertexFilterActive", &GraphInterface::IsVertexFilterActive)
......
...@@ -1246,7 +1246,9 @@ class Graph(object): ...@@ -1246,7 +1246,9 @@ class Graph(object):
def add_vertex(self, n=1): def add_vertex(self, n=1):
"""Add a vertex to the graph, and return it. If ``n > 1``, ``n`` """Add a vertex to the graph, and return it. If ``n > 1``, ``n``
vertices are inserted and an iterator over the new vertices is returned.""" vertices are inserted and an iterator over the new vertices is returned.
This operation is :math:`O(n)`.
"""
self.__check_perms("add_vertex") self.__check_perms("add_vertex")
v = libcore.add_vertex(weakref.ref(self), n) v = libcore.add_vertex(weakref.ref(self), n)
...@@ -1304,7 +1306,7 @@ class Graph(object): ...@@ -1304,7 +1306,7 @@ class Graph(object):
def add_edge(self, source, target): def add_edge(self, source, target):
"""Add a new edge from ``source`` to ``target`` to the graph, and return """Add a new edge from ``source`` to ``target`` to the graph, and return
it.""" it. This operation is :math:`O(1)`."""
self.__check_perms("add_edge") self.__check_perms("add_edge")
e = libcore.add_edge(weakref.ref(self), self.vertex(int(source)), e = libcore.add_edge(weakref.ref(self), self.vertex(int(source)),
self.vertex(int(target))) self.vertex(int(target)))
...@@ -1314,10 +1316,36 @@ class Graph(object): ...@@ -1314,10 +1316,36 @@ class Graph(object):
return e return e
def remove_edge(self, edge): def remove_edge(self, edge):
"""Remove an edge from the graph.""" r"""Remove an edge from the graph.
.. note::
This operation is normally :math:`O(k_s + k_t)`, where :math:`k_s`
and :math:`k_s` are the total degrees of the source and target
vertices, respectively. However, if :method:`~Graph.set_fast_edge_removal`
is set to `True`, this operation becomes :math:`O(1)`.
.. warning::
The relative ordering of the remaining edges in the graph is kept
unchanged, unless :method:`~Graph.set_fast_edge_removal` is set to
`True`, in which case it can change.
"""
self.__check_perms("del_edge") self.__check_perms("del_edge")
return libcore.remove_edge(self.__graph, edge) return libcore.remove_edge(self.__graph, edge)
def set_fast_edge_removal(self, fast=True):
r"""If ``fast == True`` the fast :math:`O(1)` removal of edges will be
enabled. This requires an additional data structure of size :math:`O(E)`
to be kept at all times. If ``fast == False``, this data structure is
destroyed."""
self.__graph.SetKeepEpos(fast)
def get_fast_edge_removal(self):
r"""Return whether the fast :math:`O(1)` removal of edges is currently
enabled."""
return self.__graph.GetKeepEpos()
def clear(self): def clear(self):
"""Remove all vertices and edges from the graph.""" """Remove all vertices and edges from the graph."""
self.__check_perms("del_vertex") self.__check_perms("del_vertex")
...@@ -2111,7 +2139,7 @@ Vector_string.get_array = lambda self: None ...@@ -2111,7 +2139,7 @@ Vector_string.get_array = lambda self: None
Vector_string.__repr__ = lambda self: repr(list(self)) Vector_string.__repr__ = lambda self: repr(list(self))
# Global RNG
_rng = libcore.get_rng(numpy.random.randint(0, sys.maxsize)) _rng = libcore.get_rng(numpy.random.randint(0, sys.maxsize))
......
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