Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Tiago Peixoto
graph-tool
Commits
bd96105e
Commit
bd96105e
authored
Feb 05, 2015
by
Tiago Peixoto
Browse files
Include support for removal of vertex sequences
This fixes #189
parent
0bc62ada
Changes
5
Hide whitespace changes
Inline
Side-by-side
doc/quickstart.rst
View file @
bd96105e
...
...
@@ -143,6 +143,22 @@ descriptors:
>>> print(len(list(vlist)))
10
Each vertex in a graph has an unique index, which is *always* between
:math:``0`` and :math:``N-1``, where :math:``N`` is the number of
vertices. This index can be obtained by using the
:attr:`~graph_tool.Graph.vertex_index` attribute of the graph (which is
a *property map*, see :ref:`sec_property_maps`), or by converting the
vertex descriptor to an ``int``.
.. doctest::
>>> v = g.add_vertex()
>>> print(g.vertex_index[v])
11
>>> print(int(v))
11
Edges and vertices can also be removed at any time with the
:meth:`~graph_tool.Graph.remove_vertex` and :meth:`~graph_tool.Graph.remove_edge` methods,
...
...
@@ -151,21 +167,48 @@ Edges and vertices can also be removed at any time with the
>>> g.remove_edge(e) # e no longer exists
>>> g.remove_vertex(v2) # the second vertex is also gone
.. note::
Removing a vertex is an :math:`O(N)` operation. The vertices are
internally stored in a `STL vector <http://en.wikipedia.org/wiki/Vector_%28STL%29>`_,
so removing an element somewhere in the middle of the list requires
the shifting of the rest of the list. Thus, fast :math:`O(1)`
removals are only possible either if one can guarantee that only
vertices in the end of the list are removed (the ones last added to
the graph), or if the relative vertex ordering is invalidated. This
last behavior can be achieved by passing the option ``fast == True``,
to :meth:`~graph_tool.Graph.remove_vertex`, which causes vertex
Removing a vertex is typically an :math:`O(N)` operation. The
vertices are internally stored in a `STL vector
<http://en.wikipedia.org/wiki/Vector_%28STL%29>`_, so removing an
element somewhere in the middle of the list requires the shifting of
the rest of the list. Thus, fast :math:`O(1)` removals are only
possible either if one can guarantee that only vertices in the end of
the list are removed (the ones last added to the graph), or if the
relative vertex ordering is invalidated. This last behavior can be
achieved by passing the option ``fast == True``, to
:meth:`~graph_tool.Graph.remove_vertex`, which causes the vertex
being deleted to be 'swapped' with the last vertex (i.e. with the
largest index), which will in turn inherit the index of the vertex
being deleted.
.. warning::
Because of the above, removing a vertex with an index smaller than
:math:`N-1` will **invalidate either the last** (``fast = True``)
**or all** (``fast = False``) **descriptors pointing to vertices with
higher index**.
As a consequence, if more than one vertex is to be removed at a given
time, they should **always** be removed in decreasing index order:
.. code::
# 'del_list' is a list of vertex descriptors
for v in reversed(sorted(del_list)):
g.remove_vertex(v)
Alternatively (and preferably), a list (or any iterable) may be
passed directly as the ``vertex`` parameter of the
:meth:`~graph_tool.Graph.remove_vertex` function, and the above is
performed internally (in C++).
Note that property map values (see :ref:`sec_property_maps`) are
unaffected by the index changes due to vertex removal.
.. note::
Removing an edge is an :math:`O(k_{s} + k_{t})` operation, where
:math:`k_{s}` is the out-degree of the source vertex, and
...
...
@@ -174,26 +217,7 @@ Edges and vertices can also be removed at any time with the
`True`, in which case it becomes :math:`O(1)`, at the expense of
additional data of size :math:`O(E)`.
Each vertex in a graph has an unique index, which is numbered from 0 to
N-1, where N is the number of vertices. This index can be obtained by
using the :attr:`~graph_tool.Graph.vertex_index` attribute of the graph
(which is a *property map*, see :ref:`sec_property_maps`), or by
converting the vertex descriptor to an ``int``.
.. doctest::
>>> v = g.add_vertex()
>>> print(g.vertex_index[v])
11
>>> print(int(v))
11
.. note::
Removing a vertex will cause the index of any vertex with a larger
index to be changed, so that the indexes *always* conform to the
:math:`[0, N-1]` range. However, property map values (see
:ref:`sec_property_maps`) are unaffected.
No edge descriptors are ever invalidated after edge removal.
Since vertices are uniquely identifiable by their indexes, there is no
need to keep the vertex descriptor lying around to access them at a
...
...
@@ -238,6 +262,7 @@ its index will be "vacant", and the remaining indexes will be left
unmodified, and thus will not lie in the range :math:`[0, E-1]`. If a
new edge is added, it will reuse old indexes, in an increasing order.
.. _sec_iteration:
Iterating over vertices and edges
...
...
@@ -306,7 +331,7 @@ vertices in the graph.
somewhere (such as in a list) and remove them only after no iterator
is being used. Removal during iteration will cause bad things to
happen.
.. _sec_property_maps:
...
...
src/graph/graph.hh
View file @
bd96105e
...
...
@@ -101,8 +101,8 @@ public:
void
PurgeEdges
();
// removes filtered edges
void
Clear
();
void
ClearEdges
();
void
ShiftVertexProperty
(
boost
::
any
map
,
size_
t
index
)
const
;
void
MoveVertexProperty
(
boost
::
any
map
,
size_
t
index
)
const
;
void
ShiftVertexProperty
(
boost
::
any
map
,
boost
::
python
::
objec
t
o
index
)
const
;
void
MoveVertexProperty
(
boost
::
any
map
,
boost
::
python
::
objec
t
o
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
);
...
...
src/graph/graph_properties.cc
View file @
bd96105e
...
...
@@ -45,15 +45,20 @@ const char* type_names[] =
struct
shift_vertex_property
{
template
<
class
PropertyMap
>
template
<
class
PropertyMap
,
class
Vec
>
void
operator
()(
PropertyMap
,
const
GraphInterface
::
multigraph_t
&
g
,
boost
::
any
map
,
size_t
vi
,
bool
&
found
)
const
boost
::
any
map
,
const
Vec
&
vi
,
bool
&
found
)
const
{
try
{
PropertyMap
pmap
=
any_cast
<
PropertyMap
>
(
map
);
for
(
size_t
i
=
vi
;
i
<
num_vertices
(
g
)
-
1
;
++
i
)
pmap
[
vertex
(
i
,
g
)]
=
pmap
[
vertex
(
i
+
1
,
g
)];
size_t
back
=
num_vertices
(
g
)
-
1
;
for
(
auto
v
:
vi
)
{
for
(
size_t
i
=
v
;
i
<
back
;
++
i
)
pmap
[
vertex
(
i
,
g
)]
=
pmap
[
vertex
(
i
+
1
,
g
)];
back
--
;
}
found
=
true
;
}
catch
(
bad_any_cast
&
)
{}
...
...
@@ -61,8 +66,9 @@ struct shift_vertex_property
};
// this function will shift all the properties when a vertex is to be deleted
void
GraphInterface
::
ShiftVertexProperty
(
boost
::
any
prop
,
size_
t
index
)
const
void
GraphInterface
::
ShiftVertexProperty
(
boost
::
any
prop
,
python
::
objec
t
o
index
)
const
{
boost
::
multi_array_ref
<
int64_t
,
1
>
index
=
get_array
<
int64_t
,
1
>
(
oindex
);
bool
found
=
false
;
mpl
::
for_each
<
writable_vertex_properties
>
(
std
::
bind
(
shift_vertex_property
(),
std
::
placeholders
::
_1
,
std
::
ref
(
*
_mg
),
...
...
@@ -73,14 +79,19 @@ void GraphInterface::ShiftVertexProperty(boost::any prop, size_t index) const
struct
move_vertex_property
{
template
<
class
PropertyMap
>
template
<
class
PropertyMap
,
class
Vec
>
void
operator
()(
PropertyMap
,
const
GraphInterface
::
multigraph_t
&
g
,
boost
::
any
map
,
size_t
vi
,
size_t
back
,
bool
&
found
)
const
boost
::
any
map
,
const
Vec
&
vi
,
size_t
back
,
bool
&
found
)
const
{
try
{
PropertyMap
pmap
=
any_cast
<
PropertyMap
>
(
map
);
pmap
[
vertex
(
vi
,
g
)]
=
pmap
[
vertex
(
back
,
g
)];
for
(
auto
v
:
vi
)
{
pmap
[
vertex
(
v
,
g
)]
=
pmap
[
vertex
(
back
,
g
)];
back
--
;
}
found
=
true
;
}
catch
(
bad_any_cast
&
)
{}
...
...
@@ -88,8 +99,9 @@ struct move_vertex_property
};
// 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
void
GraphInterface
::
MoveVertexProperty
(
boost
::
any
prop
,
python
::
objec
t
o
index
)
const
{
boost
::
multi_array_ref
<
int64_t
,
1
>
index
=
get_array
<
int64_t
,
1
>
(
oindex
);
size_t
back
=
num_vertices
(
*
_mg
)
-
1
;
bool
found
=
false
;
mpl
::
for_each
<
writable_vertex_properties
>
...
...
src/graph/graph_python_interface.cc
View file @
bd96105e
...
...
@@ -142,33 +142,21 @@ python::object add_vertex(python::object g, size_t n)
return
python
::
object
(
PythonVertex
(
g
,
add_vertex
(
gi
.
GetGraph
())));
}
struct
shift_vertex_property
{
template
<
class
Graph
,
class
PropertyMap
>
void
operator
()(
const
Graph
&
g
,
size_t
vi
,
PropertyMap
pmap
)
const
{
size_t
N
=
num_vertices
(
g
);
if
(
N
<=
1
||
vi
>=
N
-
1
)
return
;
for
(
size_t
i
=
vi
;
i
<
N
-
1
;
++
i
)
{
pmap
[
vertex
(
i
,
g
)]
=
pmap
[
vertex
(
i
+
1
,
g
)];
}
}
};
void
remove_vertex
(
GraphInterface
&
gi
,
const
python
::
object
&
v
,
bool
fast
)
void
remove_vertex
(
GraphInterface
&
gi
,
const
python
::
object
&
oindex
,
bool
fast
)
{
PythonVertex
&
pv
=
python
::
extract
<
PythonVertex
&>
(
v
);
pv
.
CheckValid
();
GraphInterface
::
vertex_t
dv
=
pv
.
GetDescriptor
();
pv
.
SetValid
(
false
);
boost
::
multi_array_ref
<
int64_t
,
1
>
index
=
get_array
<
int64_t
,
1
>
(
oindex
);
auto
&
g
=
gi
.
GetGraph
();
if
(
fast
)
remove_vertex_fast
(
dv
,
gi
.
GetGraph
());
{
for
(
auto
v
:
index
)
remove_vertex_fast
(
vertex
(
v
,
g
),
g
);
}
else
remove_vertex
(
dv
,
gi
.
GetGraph
());
{
for
(
auto
v
:
index
)
remove_vertex
(
vertex
(
v
,
g
),
g
);
}
}
struct
add_new_edge
...
...
src/graph_tool/__init__.py
View file @
bd96105e
...
...
@@ -1039,7 +1039,7 @@ def edge_endpoint_property(g, prop, endpoint, eprop=None):
@
_limit_args
({
"htype"
:
[
"int8_t"
,
"int32_t"
,
"int64_t"
]})
def
perfect_prop_hash
(
props
,
htype
=
"int32_t"
):
"""Given a list of property maps `props` of the same type, a derived list of
property maps with integral type `htype` is retured, where each value is
property maps with integral type `htype` is retur
n
ed, where each value is
replaced by a perfect (i.e. unique) hash value.
.. note::
...
...
@@ -1431,7 +1431,8 @@ class Graph(object):
return
(
self
.
vertex
(
i
)
for
i
in
range
(
pos
,
pos
+
n
))
def
remove_vertex
(
self
,
vertex
,
fast
=
False
):
r
"""Remove a vertex from the graph.
r
"""Remove a vertex from the graph. If ``vertex`` is an iterable, it
should correspond to a sequence of vertices to be removed.
.. note::
...
...
@@ -1441,6 +1442,27 @@ class Graph(object):
degree of the vertex being deleted, and :math:`k_{\text{last}}` is
the (total) degree of the vertex with the largest index.
.. warning::
This operation may invalidate vertex descriptors. Vertices are always
indexed contiguously in the range :math:`[0, N-1]`, hence vertex
descriptors with an index higher than ``vertex`` will be invalidated
after removal (if ``fast == False``, otherwise only descriptors
pointing to vertices with the largest index will be invalidated).
Because of this, the only safe way to remove more than one vertex at
once is to sort them in decreasing index order:
.. code::
# 'del_list' is a list of vertex descriptors
for v in reversed(sorted(del_list)):
g.remove_vertex(v)
Alternatively (and preferably), a list (or iterable) may be passed
directly as the ``vertex`` parameter, and the above is performed
internally (in C++).
.. warning::
If ``fast == True``, the vertex being deleted is 'swapped' with the
...
...
@@ -1451,20 +1473,35 @@ class Graph(object):
"""
self
.
__check_perms
(
"del_vertex"
)
vertex
=
self
.
vertex
(
int
(
vertex
))
index
=
self
.
vertex_index
[
vertex
]
try
:
vs
=
numpy
.
array
([
int
(
vertex
)],
dtype
=
"int64"
)
except
TypeError
:
try
:
vs
=
numpy
.
asarray
(
vertex
,
dtype
=
"int64"
)
except
TypeError
:
vs
=
numpy
.
asarray
([
int
(
v
)
for
v
in
vertex
],
dtype
=
"int64"
)
if
len
(
vs
)
==
0
:
return
vs
=
numpy
.
sort
(
vs
)[::
-
1
]
back
=
self
.
__graph
.
GetNumberOfVertices
(
False
)
-
1
if
vs
.
max
()
>
back
:
raise
ValueError
(
"Vertex index %d is invalid"
%
vs
.
max
())
# move / shift all known property maps
if
index
!=
back
:
if
len
(
vs
)
>
0
or
vs
[
0
]
!=
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
)
self
.
__graph
.
MoveVertexProperty
(
pmap
().
_PropertyMap__map
.
get_map
(),
vs
)
else
:
self
.
__graph
.
ShiftVertexProperty
(
pmap
().
_PropertyMap__map
.
get_map
(),
index
)
self
.
__graph
.
ShiftVertexProperty
(
pmap
().
_PropertyMap__map
.
get_map
(),
vs
)
libcore
.
remove_vertex
(
self
.
__graph
,
v
ertex
,
fast
)
libcore
.
remove_vertex
(
self
.
__graph
,
v
s
,
fast
)
def
clear_vertex
(
self
,
vertex
):
"""Remove all in and out-edges from the given vertex."""
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment