Commit f7bfd064 authored by Tiago Peixoto's avatar Tiago Peixoto

Split PropertyMap into Vertex/Edge/GraphPropertyMap

This improves performance of the Python API.
parent d04f8187
......@@ -198,6 +198,12 @@
.. autoclass:: Vertex
.. autoclass:: Edge
.. autoclass:: PropertyMap
.. autoclass:: VertexPropertyMap
:show-inheritance:
.. autoclass:: EdgePropertyMap
:show-inheritance:
.. autoclass:: GraphPropertyMap
:show-inheritance:
.. autoclass:: PropertyArray
:show-inheritance:
:no-members:
......
......@@ -373,10 +373,11 @@ Property maps
Property maps are a way of associating additional information to the
vertices, edges or to the graph itself. There are thus three types of
property maps: vertex, edge and graph. All of them are handled by the
same class, :class:`~graph_tool.PropertyMap`. Each created property map
has an associated *value type*, which must be chosen from the predefined
set:
property maps: vertex, edge and graph. They are handled by the
classes :class:`~graph_tool.VertexPropertyMap`,
:class:`~graph_tool.EdgePropertyMap`, and
:class:`~graph_tool.GraphPropertyMap`. Each created property map has an
associated *value type*, which must be chosen from the predefined set:
.. tabularcolumns:: |l|l|
......@@ -402,10 +403,15 @@ set:
``python::object`` ``object``
======================== ======================
New property maps can be created for a given graph by calling the
:meth:`~graph_tool.Graph.new_vertex_property`, :meth:`~graph_tool.Graph.new_edge_property`, or
:meth:`~graph_tool.Graph.new_graph_property`, for each map type. The values are then
accessed by vertex or edge descriptors, or the graph itself, as such:
New property maps can be created for a given graph by calling one of the
methods :meth:`~graph_tool.Graph.new_vertex_property` (alias
:meth:`~graph_tool.Graph.new_vp`),
:meth:`~graph_tool.Graph.new_edge_property` (alias
:meth:`~graph_tool.Graph.new_ep`), or
:meth:`~graph_tool.Graph.new_graph_property` (alias
:meth:`~graph_tool.Graph.new_gp`), for each map type. The values are
then accessed by vertex or edge descriptors, or the graph itself, as
such:
.. doctest::
......@@ -414,26 +420,30 @@ accessed by vertex or edge descriptors, or the graph itself, as such:
g = Graph()
g.add_vertex(100)
# insert some random links
for s,t in izip(randint(0, 100, 100), randint(0, 100, 100)):
g.add_edge(g.vertex(s), g.vertex(t))
vprop_double = g.new_vertex_property("double") # Double-precision floating point
vprop_double[g.vertex(10)] = 3.1416
v = g.vertex(10)
vprop_double[v] = 3.1416
vprop_vint = g.new_vertex_property("vector<int>") # Vector of ints
vprop_vint[g.vertex(40)] = [1, 3, 42, 54]
v = g.vertex(40)
vprop_vint[v] = [1, 3, 42, 54]
eprop_dict = g.new_edge_property("object") # Arbitrary python object.
eprop_dict[g.edges().next()] = {"foo": "bar", "gnu": 42} # In this case, a dict.
e = g.edges().next()
eprop_dict[e] = {"foo": "bar", "gnu": 42} # In this case, a dict.
gprop_bool = g.new_graph_property("bool") # Boolean
gprop_bool = g.new_graph_property("bool") # Boolean
gprop_bool[g] = True
Property maps with scalar value types can also be accessed as a
:class:`numpy.ndarray`, with the
:meth:`~graph_tool.PropertyMap.get_array` method, or the
:attr:`~graph_tool.PropertyMap.a` attribute, i.e.,
:attr:`~graph_tool.PropertyMap.a` attribute, e.g.,
.. doctest::
......
......@@ -257,35 +257,15 @@ struct convert
{
Type1 operator()(const Type2& v) const
{
return do_convert(v, std::is_convertible<Type2,Type1>());
}
Type1 do_convert(const Type2& v, std::true_type) const
{
return Type1(v);
}
Type1 do_convert(const Type2& v, std::false_type) const
{
return specific_convert<Type1,Type2>()(v);
}
template <class T1, class T2>
struct specific_convert
{
T1 operator()(const T2&) const
if constexpr (std::is_same<Type1, boost::python::object>::value)
{
throw boost::bad_lexical_cast(); // default action
return boost::python::object(v);
}
};
// specific specializations
// python::object
template <class T1>
struct specific_convert<T1,boost::python::object>
{
T1 operator()(const boost::python::object& v) const
else if constexpr (std::is_convertible<Type2,Type1>::value)
{
return Type1(v);
}
else if constexpr (std::is_same<Type2, boost::python::object>::value)
{
boost::python::extract<Type1> x(v);
if (x.check())
......@@ -293,35 +273,40 @@ struct convert
else
throw boost::bad_lexical_cast();
}
};
// std::string
template <class T1>
struct specific_convert<T1,std::string>
{
T1 operator()(const std::string& v) const
else if constexpr (std::is_same<Type2, std::string>::value)
{
//uint8_t is not char, it is bool!
if (std::is_same<T1, uint8_t>::value)
return convert<T1,int>()(boost::lexical_cast<int>(v));
if (std::is_same<Type1, uint8_t>::value)
return convert<Type1,int>()(boost::lexical_cast<int>(v));
else
return boost::lexical_cast<T1>(v);
return boost::lexical_cast<Type1>(v);
}
};
template <class T2>
struct specific_convert<std::string,T2>
{
std::string operator()(const T2& v) const
else if constexpr (std::is_same<Type1, std::string>::value)
{
//uint8_t is not char, it is bool!
if (std::is_same<T2, uint8_t>::value)
return boost::lexical_cast<std::string>(convert<int,T2>()(v));
if (std::is_same<Type2, uint8_t>::value)
return boost::lexical_cast<std::string>(convert<int,Type2>()(v));
else
return boost::lexical_cast<std::string>(v);
}
else
{
return specific_convert<Type1, Type2>()(v);
}
}
// default action
template <class T1, class T2>
struct specific_convert
{
T1 operator()(const T2&) const
{
throw boost::bad_lexical_cast();
}
};
// specific specializations
// vectors
template <class T1, class T2>
struct specific_convert<std::vector<T1>, std::vector<T2>>
......@@ -338,20 +323,6 @@ struct convert
};
// python::object to std::string, to solve ambiguity
template<> template<>
struct convert<std::string,boost::python::object>::specific_convert<std::string,boost::python::object>
{
std::string operator()(const boost::python::object& v) const
{
boost::python::extract<std::string> x(v);
if (x.check())
return x();
else
throw boost::bad_lexical_cast();
}
};
// No op
template <class Type1>
struct convert<Type1, Type1>
......@@ -435,30 +406,30 @@ private:
}
template <class PMap>
Value get_dispatch(PMap pmap, const typename boost::property_traits<PMap>::key_type& k,
Value get_dispatch(PMap&& pmap, const typename boost::property_traits<std::remove_reference_t<PMap>>::key_type& k,
std::true_type)
{
return _c_get(boost::get(pmap, k));
}
template <class PMap>
Value get_dispatch(PMap, const typename boost::property_traits<PMap>::key_type&,
Value get_dispatch(PMap&&, const typename boost::property_traits<std::remove_reference_t<PMap>>::key_type&,
std::false_type)
{
throw graph_tool::ValueException("Property map is not readable.");
}
template <class PMap>
void put_dispatch(PMap pmap, const typename boost::property_traits<PMap>::key_type& k,
typename boost::property_traits<PMap>::value_type val,
void put_dispatch(PMap&& pmap, const typename boost::property_traits<std::remove_reference_t<PMap>>::key_type& k,
typename boost::property_traits<std::remove_reference_t<PMap>>::value_type val,
std::true_type)
{
boost::put(pmap, k, val);
}
template <class PMap>
void put_dispatch(PMap, const typename boost::property_traits<PMap>::key_type&,
typename boost::property_traits<PMap>::value_type,
void put_dispatch(PMap&&, const typename boost::property_traits<std::remove_reference_t<PMap>>::key_type&,
typename boost::property_traits<std::remove_reference_t<PMap>>::value_type,
std::false_type)
{
throw ValueException("Property map is not writable.");
......
This diff is collapsed.
......@@ -459,33 +459,34 @@ public:
template <class PythonDescriptor>
reference get_value(const PythonDescriptor& key)
{
key.check_valid();
//key.check_valid();
return get(_pmap, key.get_descriptor());
}
// in this case, val should be a copy, not a reference. This is to avoid a
// problem with vector-valued property maps
template <class PythonDescriptor>
void set_value(const PythonDescriptor& key, value_type val)
reference get_value_int(size_t v)
{
set_value_dispatch(key, val,
std::is_convertible<typename boost::property_traits<PropertyMap>::category,
boost::writable_property_map_tag>());
return get(_pmap, v);
}
// in this case, val should be a copy, not a reference. This is to avoid a
// problem with vector-valued property maps
template <class PythonDescriptor>
void set_value_dispatch(const PythonDescriptor& key, const value_type& val,
std::true_type)
void set_value(const PythonDescriptor& key, value_type val)
{
key.check_valid();
put(_pmap, key.get_descriptor(), val);
if constexpr (std::is_convertible<typename boost::property_traits<PropertyMap>::category,
boost::writable_property_map_tag>::value)
put(_pmap, key.get_descriptor(), val);
else
throw ValueException("property is read-only");
}
template <class PythonDescriptor>
void set_value_dispatch(const PythonDescriptor&, const value_type&,
std::false_type)
void set_value_int(size_t v, value_type val)
{
throw ValueException("property is read-only");
if constexpr (std::is_convertible<typename boost::property_traits<PropertyMap>::category,
boost::writable_property_map_tag>::value)
put(_pmap, v, val);
else
throw ValueException("property is read-only");
}
size_t get_hash() const
......
......@@ -91,9 +91,9 @@ struct export_vertex_property_map
void operator()(Graph*, PClass& pclass, ReturnPolicy return_policy) const
{
pclass
.def("__getitem__", &pmap_t::template get_value<PythonVertex<Graph>>,
.def("__getitem__", &pmap_t::get_value_int,
return_policy)
.def("__setitem__", &pmap_t::template set_value<PythonVertex<Graph>>);
.def("__setitem__", &pmap_t::set_value_int);
}
};
};
......
This diff is collapsed.
......@@ -430,7 +430,7 @@ def graphviz_draw(g, pos=None, size=(15, 15), pin=False, layout=None,
else:
n = libgv.agnode(gvg, str(int(v)).encode("utf8"))
if type(vsize) == PropertyMap:
if isinstance(vsize, PropertyMap):
vw = vh = vsize[v]
else:
vw = vh = vsize
......
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