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
3db1d2d5
Commit
3db1d2d5
authored
Jul 09, 2020
by
Tiago Peixoto
Browse files
Graph.add_edge_list(): improve handling of hashed=True for arbitrary types
parent
541f949c
Pipeline
#672
passed with stage
in 666 minutes and 7 seconds
Changes
3
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
src/graph/graph_python_interface.cc
View file @
3db1d2d5
...
...
@@ -854,7 +854,7 @@ void do_add_edge_list(GraphInterface& gi, python::object aedge_list,
python
::
object
eprops
);
void
do_add_edge_list_hashed
(
GraphInterface
&
gi
,
python
::
object
aedge_list
,
boost
::
any
&
vertex_map
,
bool
is_str
,
boost
::
any
&
vertex_map
,
python
::
object
eprops
);
void
do_add_edge_list_iter
(
GraphInterface
&
gi
,
python
::
object
edge_list
,
...
...
src/graph/graph_python_interface_imp1.cc
View file @
3db1d2d5
...
...
@@ -117,285 +117,195 @@ void do_add_edge_list(GraphInterface& gi, python::object aedge_list,
throw
GraphException
(
"Invalid type for edge list; must be two-dimensional with a scalar type"
);
}
template
<
class
ValueList
>
struct
add_edge_list_
hash
struct
add_edge_list_
iter
{
template
<
class
Graph
,
class
VProp
>
void
operator
()(
Graph
&
g
,
python
::
object
a
edge_list
,
VProp
vmap
,
bool
&
found
,
bool
use_str
,
python
::
object
&
eprops
)
const
template
<
class
Graph
>
void
operator
()(
Graph
&
g
,
python
::
object
&
edge_list
,
python
::
object
&
o
eprops
)
const
{
boost
::
mpl
::
for_each
<
ValueList
>
(
std
::
bind
(
dispatch
(),
std
::
ref
(
g
),
std
::
ref
(
aedge_list
),
std
::
ref
(
vmap
),
std
::
ref
(
found
),
std
::
ref
(
eprops
),
std
::
placeholders
::
_1
));
if
(
!
found
)
{
if
(
use_str
)
dispatch
()(
g
,
aedge_list
,
vmap
,
found
,
eprops
,
std
::
string
());
else
dispatch
()(
g
,
aedge_list
,
vmap
,
found
,
eprops
,
python
::
object
());
}
}
typedef
typename
graph_traits
<
Graph
>::
edge_descriptor
edge_t
;
vector
<
DynamicPropertyMapWrap
<
python
::
object
,
edge_t
>>
eprops
;
python
::
stl_input_iterator
<
boost
::
any
>
piter
(
oeprops
),
pend
;
for
(;
piter
!=
pend
;
++
piter
)
eprops
.
emplace_back
(
*
piter
,
writable_edge_properties
());
struct
dispatch
{
template
<
class
Graph
,
class
VProp
,
class
Value
>
void
operator
()(
Graph
&
g
,
python
::
object
&
aedge_list
,
VProp
&
vmap
,
bool
&
found
,
python
::
object
&
oeprops
,
Value
)
const
python
::
stl_input_iterator
<
python
::
object
>
iter
(
edge_list
),
end
;
for
(;
iter
!=
end
;
++
iter
)
{
if
(
found
)
return
;
try
{
boost
::
multi_array_ref
<
Value
,
2
>
edge_list
=
get_array
<
Value
,
2
>
(
aedge_list
);
unordered_map
<
Value
,
size_t
>
vertices
;
if
(
edge_list
.
shape
()[
1
]
<
2
)
throw
GraphException
(
"Second dimension in edge list must be of size (at least) two"
);
typedef
typename
graph_traits
<
Graph
>::
edge_descriptor
edge_t
;
vector
<
DynamicPropertyMapWrap
<
Value
,
edge_t
>>
eprops
;
python
::
stl_input_iterator
<
boost
::
any
>
iter
(
oeprops
),
end
;
for
(;
iter
!=
end
;
++
iter
)
eprops
.
emplace_back
(
*
iter
,
writable_edge_properties
());
size_t
n_props
=
std
::
min
(
eprops
.
size
(),
edge_list
.
shape
()[
1
]
-
2
);
const
auto
&
row
=
*
iter
;
python
::
stl_input_iterator
<
python
::
object
>
eiter
(
row
),
eend
;
auto
get_vertex
=
[
&
]
(
const
Value
&
r
)
->
size_t
{
auto
iter
=
vertices
.
find
(
r
);
if
(
iter
==
vertices
.
end
())
{
auto
v
=
add_vertex
(
g
);
vertices
[
r
]
=
v
;
vmap
[
v
]
=
lexical_cast
<
typename
property_traits
<
VProp
>::
value_type
>
(
r
);
return
v
;
}
return
iter
->
second
;
};
size_t
s
=
0
;
size_t
t
=
0
;
for
(
const
auto
&
e
:
edge_list
)
typename
graph_traits
<
Graph
>::
edge_descriptor
e
;
size_t
i
=
0
;
for
(;
eiter
!=
eend
;
++
eiter
)
{
if
(
i
>=
eprops
.
size
()
+
2
)
break
;
const
auto
&
val
=
*
eiter
;
switch
(
i
)
{
size_t
s
=
get_vertex
(
e
[
0
]);
size_t
t
=
get_vertex
(
e
[
1
]);
auto
ne
=
add_edge
(
vertex
(
s
,
g
),
vertex
(
t
,
g
),
g
).
first
;
for
(
size_t
i
=
0
;
i
<
n_props
;
++
i
)
case
0
:
s
=
python
::
extract
<
size_t
>
(
val
);
while
(
s
>=
num_vertices
(
g
))
add_vertex
(
g
);
break
;
case
1
:
t
=
python
::
extract
<
size_t
>
(
val
);
while
(
t
>=
num_vertices
(
g
))
add_vertex
(
g
);
e
=
add_edge
(
vertex
(
s
,
g
),
vertex
(
t
,
g
),
g
).
first
;
break
;
default:
try
{
try
{
put
(
eprops
[
i
],
ne
,
e
[
i
+
2
]);
}
catch
(
bad_lexical_cast
&
)
{
throw
ValueException
(
"Invalid edge property value: "
+
lexical_cast
<
string
>
(
e
[
i
+
2
]));
}
put
(
eprops
[
i
-
2
],
e
,
val
);
}
catch
(
bad_lexical_cast
&
)
{
throw
ValueException
(
"Invalid edge property value: "
+
python
::
extract
<
string
>
(
python
::
str
(
val
))());
}
}
found
=
true
;
i
++
;
}
catch
(
InvalidNumpyConversion
&
e
)
{}
}
}
};
template
<
class
Graph
,
class
VProp
>
void
operator
()(
Graph
&
g
,
python
::
object
&
edge_list
,
VProp
&
vmap
,
bool
&
found
,
python
::
object
&
oeprops
,
std
::
string
)
const
void
do_add_edge_list_iter
(
GraphInterface
&
gi
,
python
::
object
edge_list
,
python
::
object
eprops
)
{
run_action
<>
()
(
gi
,
[
&
](
auto
&&
graph
)
{
return
add_edge_list_iter
()
(
std
::
forward
<
decltype
(
graph
)
>
(
graph
),
edge_list
,
eprops
);
})();
}
struct
add_edge_list_hash
{
template
<
class
Graph
,
class
VProp
>
void
operator
()(
Graph
&
g
,
python
::
object
aedge_list
,
VProp
vmap
,
python
::
object
&
eprops
)
const
{
typedef
typename
property_traits
<
VProp
>::
value_type
val_t
;
if
constexpr
(
is_scalar_v
<
val_t
>
)
{
if
(
found
)
return
;
try
{
unordered_map
<
std
::
string
,
size_t
>
vertices
;
numpy_dispatch
(
g
,
aedge_list
,
vmap
,
eprops
);
}
catch
(
InvalidNumpyConversion
&
)
{
dispatch
(
g
,
aedge_list
,
vmap
,
eprops
);
}
}
else
{
dispatch
(
g
,
aedge_list
,
vmap
,
eprops
);
}
}
typedef
typename
graph_traits
<
Graph
>::
edge_descriptor
edge_t
;
vector
<
DynamicPropertyMapWrap
<
python
::
object
,
edge_
t
>>
eprops
;
python
::
stl_input_iterator
<
boost
::
any
>
piter
(
oeprops
)
,
pend
;
for
(;
piter
!=
pend
;
++
piter
)
eprops
.
emplace_back
(
*
piter
,
writable_edge_properties
())
;
template
<
class
Graph
,
class
VProp
>
void
numpy_dispatch
(
Graph
&
g
,
python
::
object
&
a
edge_
list
,
VProp
&
vmap
,
python
::
object
&
oeprops
)
const
{
typedef
typename
property_traits
<
VProp
>::
value_type
val_t
;
auto
get_vertex
=
[
&
]
(
const
std
::
string
&
r
)
->
size_t
{
auto
iter
=
vertices
.
find
(
r
);
if
(
iter
==
vertices
.
end
())
{
auto
v
=
add_vertex
(
g
);
vertices
[
r
]
=
v
;
vmap
[
v
]
=
lexical_cast
<
typename
property_traits
<
VProp
>::
value_type
>
(
r
);
return
v
;
}
return
iter
->
second
;
};
boost
::
multi_array_ref
<
val_t
,
2
>
edge_list
=
get_array
<
val_t
,
2
>
(
aedge_list
);
typedef
typename
std
::
conditional_t
<
std
::
is_integral_v
<
val_t
>
,
gt_hash_map
<
val_t
,
size_t
>
,
unordered_map
<
val_t
,
size_t
>>
vmap_t
;
vmap_t
vertices
;
python
::
stl_input_iterator
<
python
::
object
>
iter
(
edge_list
),
end
;
for
(;
iter
!=
end
;
++
iter
)
{
const
auto
&
row
=
*
iter
;
if
(
edge_list
.
shape
()[
1
]
<
2
)
throw
GraphException
(
"Second dimension in edge list must be of size (at least) two"
);
python
::
stl_input_iterator
<
python
::
object
>
eiter
(
row
),
eend
;
typedef
typename
graph_traits
<
Graph
>::
edge_descriptor
edge_t
;
vector
<
DynamicPropertyMapWrap
<
val_t
,
edge_t
>>
eprops
;
python
::
stl_input_iterator
<
boost
::
any
>
iter
(
oeprops
),
end
;
for
(;
iter
!=
end
;
++
iter
)
eprops
.
emplace_back
(
*
iter
,
writable_edge_properties
());
size_t
s
=
0
;
size_t
t
=
0
;
size_t
n_props
=
std
::
min
(
eprops
.
size
(),
edge_list
.
shape
()[
1
]
-
2
);
typename
graph_traits
<
Graph
>::
edge_descriptor
e
;
size_t
i
=
0
;
for
(;
eiter
!=
eend
;
++
eiter
)
{
if
(
i
>=
eprops
.
size
()
+
2
)
break
;
const
auto
&
val
=
*
eiter
;
switch
(
i
)
{
case
0
:
s
=
get_vertex
(
python
::
extract
<
std
::
string
>
(
val
));
while
(
s
>=
num_vertices
(
g
))
add_vertex
(
g
);
break
;
case
1
:
t
=
get_vertex
(
python
::
extract
<
std
::
string
>
(
val
));
while
(
t
>=
num_vertices
(
g
))
add_vertex
(
g
);
e
=
add_edge
(
vertex
(
s
,
g
),
vertex
(
t
,
g
),
g
).
first
;
break
;
default:
try
{
put
(
eprops
[
i
-
2
],
e
,
val
);
}
catch
(
bad_lexical_cast
&
)
{
throw
ValueException
(
"Invalid edge property value: "
+
python
::
extract
<
string
>
(
python
::
str
(
val
))());
}
}
i
++
;
}
auto
get_vertex
=
[
&
]
(
const
val_t
&
r
)
->
size_t
{
auto
iter
=
vertices
.
find
(
r
);
if
(
iter
==
vertices
.
end
())
{
auto
v
=
add_vertex
(
g
);
vertices
[
r
]
=
v
;
vmap
[
v
]
=
r
;
return
v
;
}
found
=
true
;
}
catch
(
InvalidNumpyConversion
&
e
)
{}
}
return
iter
->
second
;
};
template
<
class
Graph
,
class
VProp
>
void
operator
()(
Graph
&
g
,
python
::
object
&
edge_list
,
VProp
&
vmap
,
bool
&
found
,
python
::
object
&
oeprops
,
python
::
object
)
const
for
(
const
auto
&
e
:
edge_list
)
{
if
(
found
)
return
;
try
size_t
s
=
get_vertex
(
e
[
0
]);
size_t
t
=
get_vertex
(
e
[
1
]);
auto
ne
=
add_edge
(
vertex
(
s
,
g
),
vertex
(
t
,
g
),
g
).
first
;
for
(
size_t
i
=
0
;
i
<
n_props
;
++
i
)
{
unordered_map
<
python
::
object
,
size_t
>
vertices
;
typedef
typename
graph_traits
<
Graph
>::
edge_descriptor
edge_t
;
vector
<
DynamicPropertyMapWrap
<
python
::
object
,
edge_t
>>
eprops
;
python
::
stl_input_iterator
<
boost
::
any
>
piter
(
oeprops
),
pend
;
for
(;
piter
!=
pend
;
++
piter
)
eprops
.
emplace_back
(
*
piter
,
writable_edge_properties
());
auto
get_vertex
=
[
&
]
(
const
python
::
object
&
r
)
->
size_t
{
auto
iter
=
vertices
.
find
(
r
);
if
(
iter
==
vertices
.
end
())
{
auto
v
=
add_vertex
(
g
);
vertices
[
r
]
=
v
;
vmap
[
v
]
=
python
::
extract
<
typename
property_traits
<
VProp
>::
value_type
>
(
r
);
return
v
;
}
return
iter
->
second
;
};
python
::
stl_input_iterator
<
python
::
object
>
iter
(
edge_list
),
end
;
for
(;
iter
!=
end
;
++
iter
)
try
{
const
auto
&
row
=
*
iter
;
python
::
stl_input_iterator
<
python
::
object
>
eiter
(
row
),
eend
;
size_t
s
=
0
;
size_t
t
=
0
;
typename
graph_traits
<
Graph
>::
edge_descriptor
e
;
size_t
i
=
0
;
for
(;
eiter
!=
eend
;
++
eiter
)
{
if
(
i
>=
eprops
.
size
()
+
2
)
break
;
const
auto
&
val
=
*
eiter
;
switch
(
i
)
{
case
0
:
s
=
get_vertex
(
val
);
while
(
s
>=
num_vertices
(
g
))
add_vertex
(
g
);
break
;
case
1
:
t
=
get_vertex
(
val
);
while
(
t
>=
num_vertices
(
g
))
add_vertex
(
g
);
e
=
add_edge
(
vertex
(
s
,
g
),
vertex
(
t
,
g
),
g
).
first
;
break
;
default:
try
{
put
(
eprops
[
i
-
2
],
e
,
val
);
}
catch
(
bad_lexical_cast
&
)
{
throw
ValueException
(
"Invalid edge property value: "
+
python
::
extract
<
string
>
(
python
::
str
(
val
))());
}
}
i
++
;
}
put
(
eprops
[
i
],
ne
,
e
[
i
+
2
]);
}
catch
(
bad_lexical_cast
&
)
{
throw
ValueException
(
"Invalid edge property value: "
+
lexical_cast
<
string
>
(
e
[
i
+
2
]));
}
found
=
true
;
}
catch
(
InvalidNumpyConversion
&
e
)
{}
}
};
};
}
void
do_add_edge_list_hashed
(
GraphInterface
&
gi
,
python
::
object
aedge_list
,
boost
::
any
&
vertex_map
,
bool
is_str
,
python
::
object
eprops
)
{
typedef
mpl
::
vector
<
bool
,
char
,
uint8_t
,
uint16_t
,
uint32_t
,
uint64_t
,
int8_t
,
int16_t
,
int32_t
,
int64_t
,
uint64_t
,
double
,
long
double
>
vals_t
;
bool
found
=
false
;
run_action
<
graph_tool
::
all_graph_views
,
boost
::
mpl
::
true_
>
()
(
gi
,
[
&
](
auto
&&
graph
,
auto
&&
a2
)
{
return
add_edge_list_hash
<
vals_t
>
()(
std
::
forward
<
decltype
(
graph
)
>
(
graph
),
aedge_list
,
std
::
forward
<
decltype
(
a2
)
>
(
a2
),
found
,
is_str
,
eprops
);
},
writable_vertex_properties
())(
vertex_map
);
}
template
<
class
Graph
,
class
VProp
>
void
dispatch
(
Graph
&
g
,
python
::
object
&
edge_list
,
VProp
&
vmap
,
python
::
object
&
oeprops
)
const
{
typedef
typename
property_traits
<
VProp
>::
value_type
val_t
;
typedef
typename
std
::
conditional_t
<
std
::
is_integral_v
<
val_t
>
,
gt_hash_map
<
val_t
,
size_t
>
,
unordered_map
<
val_t
,
size_t
>>
vmap_t
;
vmap_t
vertices
;
struct
add_edge_list_iter
{
template
<
class
Graph
>
void
operator
()(
Graph
&
g
,
python
::
object
&
edge_list
,
python
::
object
&
oeprops
)
const
{
typedef
typename
graph_traits
<
Graph
>::
edge_descriptor
edge_t
;
vector
<
DynamicPropertyMapWrap
<
python
::
object
,
edge_t
>>
eprops
;
python
::
stl_input_iterator
<
boost
::
any
>
piter
(
oeprops
),
pend
;
for
(;
piter
!=
pend
;
++
piter
)
eprops
.
emplace_back
(
*
piter
,
writable_edge_properties
());
auto
get_vertex
=
[
&
]
(
const
val_t
&
r
)
->
size_t
{
auto
iter
=
vertices
.
find
(
r
);
if
(
iter
==
vertices
.
end
())
{
auto
v
=
add_vertex
(
g
);
vertices
[
r
]
=
v
;
vmap
[
v
]
=
r
;
return
v
;
}
return
iter
->
second
;
};
python
::
stl_input_iterator
<
python
::
object
>
iter
(
edge_list
),
end
;
for
(;
iter
!=
end
;
++
iter
)
{
const
auto
&
row
=
*
iter
;
python
::
stl_input_iterator
<
python
::
object
>
eiter
(
row
),
eend
;
size_t
s
=
0
;
size_t
t
=
0
;
typename
graph_traits
<
Graph
>::
edge_descriptor
e
;
size_t
i
=
0
;
for
(;
eiter
!=
eend
;
++
eiter
)
...
...
@@ -403,20 +313,23 @@ struct add_edge_list_iter
if
(
i
>=
eprops
.
size
()
+
2
)
break
;
const
auto
&
val
=
*
eiter
;
switch
(
i
)
if
(
i
<
2
)
{
case
0
:
s
=
python
::
extract
<
size_t
>
(
val
);
while
(
s
>=
num_vertices
(
g
))
val_t
x
;
if
constexpr
(
std
::
is_same_v
<
val_t
,
python
::
object
>
)
x
=
val
;
else
x
=
python
::
extract
<
val_t
>
(
val
);
auto
v
=
get_vertex
(
x
);
while
(
v
>=
num_vertices
(
g
))
add_vertex
(
g
);
break
;
case
1
:
t
=
python
::
extract
<
size_t
>
(
val
);
while
(
t
>=
num_vertices
(
g
))
add_vertex
(
g
);
e
=
add_edge
(
vertex
(
s
,
g
),
vertex
(
t
,
g
),
g
).
first
;
break
;
default:
if
(
i
==
0
)
s
=
v
;
else
e
=
add_edge
(
s
,
v
,
g
).
first
;
}
else
{
try
{
put
(
eprops
[
i
-
2
],
e
,
val
);
...
...
@@ -430,20 +343,22 @@ struct add_edge_list_iter
i
++
;
}
}
}
}
;
};
void
do_add_edge_list_iter
(
GraphInterface
&
gi
,
python
::
object
edge_list
,
python
::
object
eprops
)
void
do_add_edge_list_hashed
(
GraphInterface
&
gi
,
python
::
object
aedge_list
,
boost
::
any
&
vertex_map
,
python
::
object
eprops
)
{
run_action
<>
()
run_action
<
graph_tool
::
all_graph_views
,
boost
::
mpl
::
true_
>
()
(
gi
,
[
&
](
auto
&&
graph
)
[
&
](
auto
&&
graph
,
auto
&&
a2
)
{
return
add_edge_list_iter
()
(
std
::
forward
<
decltype
(
graph
)
>
(
graph
),
edge_list
,
eprops
);
})();
return
add_edge_list_hash
()
(
std
::
forward
<
decltype
(
graph
)
>
(
graph
),
aedge_list
,
std
::
forward
<
decltype
(
a2
)
>
(
a2
),
eprops
);
},
writable_vertex_properties
())(
vertex_map
);
}
}
// namespace graph_tool
src/graph_tool/__init__.py
View file @
3db1d2d5
...
...
@@ -2407,11 +2407,11 @@ class Graph(object):
"""
return
libcore
.
remove_edge
(
self
.
__graph
,
edge
)
def
add_edge_list
(
self
,
edge_list
,
hashed
=
False
,
string_vals
=
False
,
def
add_edge_list
(
self
,
edge_list
,
hashed
=
False
,
hash_type
=
"string"
,
eprops
=
None
):
"""Add a list of edges to the graph, given by ``edge_list``, which can
be an iterator of ``(source, target)`` pairs where both ``source`` and
``target`` are vertex indexes, or a :class:`
~
numpy.ndarray` of shape
``target`` are vertex indexes, or a :class:`numpy.ndarray` of shape
``(E,2)``, where ``E`` is the number of edges, and each line specifies a
``(source, target)`` pair. If the list references vertices which do not
exist in the graph, they will be created.
...
...
@@ -2420,10 +2420,11 @@ class Graph(object):
are not assumed to correspond to vertex indices directly. In this case
they will be mapped to vertex indices according to the order in which
they are encountered, and a vertex property map with the vertex values
is returned. If ``string_vals == True``, the algorithm assumes that the
vertex values are strings. Otherwise, they will be assumed to be numeric
if ``edge_list`` is a :class:`~numpy.ndarray`, or arbitrary python
objects if it is not.
is returned. The option ``hash_type`` will determine the expected type
used by the hash keys, and they can be any property map value type (see
:class:`PropertyMap`), unless ``edge_list`` is a :class:`numpy.ndarray`,
in which case the value of this option is ignored, and the type is
determined automatically.
If given, ``eprops`` should specify an iterable containing edge property
maps that will be filled with the remaining values at each row, if there
...
...
@@ -2465,13 +2466,10 @@ class Graph(object):
else
:
if
isinstance
(
edge_list
,
numpy
.
ndarray
):
vprop
=
self
.
new_vertex_property
(
_gt_type
(
edge_list
.
dtype
))
elif
string_vals
:
vprop
=
self
.
new_vertex_property
(
"string"
)
else
:
vprop
=
self
.
new_vertex_property
(
"object"
)
vprop
=
self
.
new_vertex_property
(
hash_type
)
libcore
.
add_edge_list_hashed
(
self
.
__graph
,
edge_list
,
_prop
(
"v"
,
self
,
vprop
),
string_vals
,
eprops
)
_prop
(
"v"
,
self
,
vprop
),
eprops
)
return
vprop
def
set_fast_edge_removal
(
self
,
fast
=
True
):
...
...
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