diff --git a/build/python/Jamfile b/build/python/Jamfile index 59efec54..98580135 100644 --- a/build/python/Jamfile +++ b/build/python/Jamfile @@ -52,6 +52,8 @@ extension bgl ../../src/python/fruchterman_reingold.cpp ../../src/python/kamada_kawai_spring_layout.cpp ../../src/python/page_rank.cpp + # Pickling support + ../../src/python/pickle.cpp ../../../python/build/boost_python ../bgl-viz : # Default build diff --git a/doc/python.html b/doc/python.html index 17b1d3a4..08f3255a 100644 --- a/doc/python.html +++ b/doc/python.html @@ -549,6 +549,9 @@ map is created.

Input/Output operations

+

The BGL graph types can be pickled from Python. Additional +file-based formats are also available for use.

+
read_graphviz(filename, node_id = "node_id")
Reads a GraphViz file diff --git a/src/python/basic_graph.cpp b/src/python/basic_graph.cpp index bb4fbe8f..01f11893 100644 --- a/src/python/basic_graph.cpp +++ b/src/python/basic_graph.cpp @@ -42,64 +42,6 @@ inline std::istream& operator>>(std::istream& in, default_color_type& c) namespace graph { namespace python { -class copying_dynamic_property_map -{ - public: - virtual ~copying_dynamic_property_map() {} - - virtual void copy_value(const any& to, const any& from) = 0; -}; - -template::value_type> -class copying_dynamic_adaptor - : public boost::detail::dynamic_property_map_adaptor, - public copying_dynamic_property_map -{ - typedef boost::detail::dynamic_property_map_adaptor inherited; - -public: - typedef typename property_traits::key_type key_type; - - explicit copying_dynamic_adaptor(const PropertyMap& property_map) - : inherited(property_map) { } - - virtual void copy_value(const any& to, const any& from) - { - boost::put(this->base(), any_cast(to), - boost::get(this->base(), any_cast(from))); - } -}; - -template -class copying_dynamic_adaptor - : public boost::detail::dynamic_property_map_adaptor, - public copying_dynamic_property_map -{ - typedef boost::detail::dynamic_property_map_adaptor inherited; - -public: - typedef typename property_traits::key_type key_type; - - explicit copying_dynamic_adaptor(const PropertyMap& property_map) - : inherited(property_map) { } - - virtual void copy_value(const any& to, const any& from) - { - boost::put(this->base(), any_cast(to), - boost::get(this->base(), any_cast(from))); - } - - virtual std::string get_string(const any& key) - { - using boost::python::extract; - using boost::python::str; - return std::string( - extract(str(boost::get(this->base(), - any_cast(key))))); - } -}; - template struct build_string_property_maps { @@ -117,13 +59,13 @@ struct build_string_property_maps if (key.type() == typeid(typename basic_graph::Vertex)) { typedef vector_property_map property_map_type; - typedef copying_dynamic_adaptor adaptor_type; + typedef python_dynamic_adaptor adaptor_type; result.reset (new adaptor_type(property_map_type(g->num_vertices(), g->get_vertex_index_map()))); } else if (key.type() == typeid(typename basic_graph::Edge)) { typedef vector_property_map property_map_type; - typedef copying_dynamic_adaptor adaptor_type; + typedef python_dynamic_adaptor adaptor_type; result.reset (new adaptor_type(property_map_type(g->num_edges(), g->get_edge_index_map()))); @@ -319,7 +261,7 @@ vector_property_map::VertexIndexMap> basic_graph::get_vertex_map(const std::string& name) { typedef vector_property_map result_type; - typedef copying_dynamic_adaptor adaptor_type; + typedef python_dynamic_adaptor adaptor_type; dynamic_properties::iterator i = dp.lower_bound(name); while (i != dp.end() && i->first == name) { @@ -348,7 +290,7 @@ basic_graph::get_vertex_map(const std::string& name) } typedef vector_property_map property_map_type; - typedef copying_dynamic_adaptor adaptor_type; + typedef python_dynamic_adaptor adaptor_type; property_map_type result(num_vertices(), get_vertex_index_map()); dp.insert(name, std::auto_ptr(new adaptor_type(result))); @@ -376,7 +318,7 @@ vector_property_map::EdgeIndexMap> basic_graph::get_edge_map(const std::string& name) { typedef vector_property_map result_type; - typedef copying_dynamic_adaptor adaptor_type; + typedef python_dynamic_adaptor adaptor_type; dynamic_properties::iterator i = dp.lower_bound(name); while (i != dp.end() && i->first == name) { @@ -404,7 +346,7 @@ basic_graph::get_edge_map(const std::string& name) } typedef vector_property_map property_map_type; - typedef copying_dynamic_adaptor adaptor_type; + typedef python_dynamic_adaptor adaptor_type; property_map_type result(num_edges(), get_edge_index_map()); dp.insert(name, std::auto_ptr(new adaptor_type(result))); @@ -453,7 +395,7 @@ void basic_graph::remove_vertex(Vertex vertex) // Update property maps for (dynamic_properties::iterator i = dp.begin(); i != dp.end(); ++i) { if (i->second->key() == typeid(Vertex)) { - dynamic_cast(*i->second). + dynamic_cast(*i->second). copy_value(vertex, Vertex(index_to_vertex.back())); } } @@ -519,7 +461,7 @@ void basic_graph::remove_edge(Edge edge) // Update property maps for (dynamic_properties::iterator i = dp.begin(); i != dp.end(); ++i) { if (i->second->key() == typeid(Edge)) { - dynamic_cast(*i->second). + dynamic_cast(*i->second). copy_value(edge, Edge(index_to_edge.back())); } } @@ -663,6 +605,8 @@ void export_basic_graph(const char* name) .def("read_graphviz", &Graph::read_graphviz) .def("write_graphviz", &Graph::write_graphviz) .def("write_graphviz", &Graph::write_graphviz_def) + // Pickling + .def_pickle(graph_pickle_suite()) ); export_in_graph(); diff --git a/src/python/basic_graph.hpp b/src/python/basic_graph.hpp index b700bf07..6e833b7f 100644 --- a/src/python/basic_graph.hpp +++ b/src/python/basic_graph.hpp @@ -350,6 +350,9 @@ class basic_graph inherited& base() { return *this; } const inherited& base() const { return *this; } + dynamic_properties& get_dynamic_properties() { return dp; } + const dynamic_properties& get_dynamic_properties() const { return dp; } + protected: void renumber_vertices(); void renumber_edges(); @@ -456,6 +459,103 @@ typename basic_graph::EdgeIndexMap get(edge_index_t, const basic_graph& g) { return g.get_edge_index_map(); } +template +struct graph_pickle_suite : boost::python::pickle_suite +{ + typedef basic_graph Graph; + typedef typename Graph::vertex_descriptor Vertex; + typedef typename Graph::edge_descriptor Edge; + + static + boost::python::tuple + getstate(boost::python::object g_obj); + + static + void + setstate(boost::python::object g_obj, boost::python::tuple state); +}; + +class python_dynamic_property_map +{ + public: + virtual ~python_dynamic_property_map() {} + + virtual void copy_value(const any& to, const any& from) = 0; + virtual boost::python::object get_python(const any& key) = 0; +}; + +template::value_type> +class python_dynamic_adaptor + : public boost::detail::dynamic_property_map_adaptor, + public python_dynamic_property_map +{ + typedef boost::detail::dynamic_property_map_adaptor inherited; + +public: + typedef typename property_traits::key_type key_type; + + explicit python_dynamic_adaptor(const PropertyMap& property_map) + : inherited(property_map) { } + + virtual void copy_value(const any& to, const any& from) + { + boost::put(this->base(), any_cast(to), + boost::get(this->base(), any_cast(from))); + } + + virtual boost::python::object get_python(const any& key) + { +#if defined(__GNUC__) && (__GNUC__ == 2) && (__GNUC_MINOR__ == 95) + return boost::get(this->base(), any_cast(key)); +#else + using boost::get; + + return boost::python::object(get(this->base(), any_cast(key))); +#endif + } +}; + +template +class python_dynamic_adaptor + : public boost::detail::dynamic_property_map_adaptor, + public python_dynamic_property_map +{ + typedef boost::detail::dynamic_property_map_adaptor inherited; + +public: + typedef typename property_traits::key_type key_type; + + explicit python_dynamic_adaptor(const PropertyMap& property_map) + : inherited(property_map) { } + + virtual void copy_value(const any& to, const any& from) + { + boost::put(this->base(), any_cast(to), + boost::get(this->base(), any_cast(from))); + } + + virtual std::string get_string(const any& key) + { + using boost::python::extract; + using boost::python::str; + return std::string( + extract(str(boost::get(this->base(), + any_cast(key))))); + } + + virtual boost::python::object get_python(const any& key) + { +#if defined(__GNUC__) && (__GNUC__ == 2) && (__GNUC_MINOR__ == 95) + return boost::get(this->base(), any_cast(key)); +#else + using boost::get; + + return get(this->base(), any_cast(key)); +#endif + } +}; + } } } // end namespace boost::graph::python #if 0 diff --git a/src/python/pickle.cpp b/src/python/pickle.cpp new file mode 100644 index 00000000..13d463a8 --- /dev/null +++ b/src/python/pickle.cpp @@ -0,0 +1,150 @@ +// Copyright 2005 The Trustees of Indiana University. + +// Use, modification and distribution is subject to the Boost Software +// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// Authors: Douglas Gregor +// Andrew Lumsdaine +#include "graph.hpp" +#include "digraph.hpp" +#include + +namespace boost { namespace graph { namespace python { + +template +boost::python::tuple +graph_pickle_suite::getstate(boost::python::object g_obj) +{ + using namespace boost::python; + using boost::python::tuple; + using boost::python::make_tuple; + using boost::python::list; + + const Graph& g = extract(g_obj)(); + typename Graph::VertexIndexMap vertex_index_map = g.get_vertex_index_map(); + typename Graph::EdgeIndexMap edge_index_map = g.get_edge_index_map(); + + dict vertex_properties; + dict edge_properties; + + // Collect edges + std::vector the_edges(g.num_edges()); + BGL_FORALL_EDGES_T(e, g, Graph) + the_edges[get(edge_index_map, e)] = + make_tuple(get(vertex_index_map, source(e, g)), + get(vertex_index_map, target(e, g))); + + list edges_list; + for (std::vector::iterator i = the_edges.begin(); + i != the_edges.end(); ++i) + edges_list.append(*i); + + // Collect vertex and edge properties + const dynamic_properties& dp = g.get_dynamic_properties(); + for (dynamic_properties::const_iterator pm = dp.begin(); + pm != dp.end(); ++pm) { + python_dynamic_property_map* pmap = + dynamic_cast(pm->second); + if (pm->second->key() == typeid(Vertex)) { + std::vector values(g.num_vertices()); + BGL_FORALL_VERTICES_T(v, g, Graph) + values[get(vertex_index_map, v)] = pmap->get_python(v); + + list values_list; + for (std::vector::iterator i = values.begin(); + i != values.end(); ++i) + values_list.append(*i); + vertex_properties[pm->first] = tuple(values_list); + } else if (pm->second->key() == typeid(Edge)) { + std::vector values(g.num_edges()); + BGL_FORALL_EDGES_T(e, g, Graph) + values[get(edge_index_map, e)] = pmap->get_python(e); + + list values_list; + for (std::vector::iterator i = values.begin(); + i != values.end(); ++i) + values_list.append(*i); + edge_properties[pm->first] = tuple(values_list); + } else { + assert(false); + } + } + + return make_tuple(g_obj.attr("__dict__"), + g.num_vertices(), + edges_list, + vertex_properties, + edge_properties); +} + +template +void +graph_pickle_suite::setstate(boost::python::object g_obj, + boost::python::tuple state) +{ + using namespace boost::python; + using boost::python::tuple; + using boost::python::make_tuple; + using boost::python::list; + + Graph& g = extract(g_obj)(); + + // restore the graph's __dict__ + dict d = extract(g_obj.attr("__dict__"))(); + d.update(state[0]); + + // Get the number of vertices + typedef typename graph_traits::vertices_size_type vertices_size_type; + vertices_size_type n = extract(state[1]); + std::vector vertices; + vertices.reserve(n); + while (vertices.size() < n) vertices.push_back(g.add_vertex()); + + // Get the edges + typedef typename graph_traits::edges_size_type edges_size_type; + std::vector the_edges; + list edges_list = extract(state[2]); + edges_size_type m = extract(edges_list.attr("__len__")()); + the_edges.reserve(m); + for (unsigned i = 0; i < m; ++i) { + tuple e = extract(edges_list[i]); + the_edges.push_back + (g.add_edge(vertices[extract(e[0])], + vertices[extract(e[1])])); + } + + // Get the vertex properties + typedef typename Graph::VertexIndexMap VertexIndexMap; + dict vertex_properties = extract(state[3]); + list vertex_map_names = vertex_properties.keys(); + while (vertex_map_names != list()) { + object name_obj = vertex_map_names.pop(0); + const char* name = extract(name_obj); + vector_property_map pmap = + g.get_vertex_object_map(name); + tuple values = extract(vertex_properties[name_obj]); + for (vertices_size_type i = 0; i < g.num_vertices(); ++i) + put(pmap, vertices[i], values[i]); + } + + // Get the edge properties + typedef typename Graph::EdgeIndexMap EdgeIndexMap; + dict edge_properties = extract(state[4]); + list edge_map_names = edge_properties.keys(); + while (edge_map_names != list()) { + object name_obj = edge_map_names.pop(0); + const char* name = extract(name_obj); + vector_property_map pmap = + g.get_edge_object_map(name); + tuple values = extract(edge_properties[name_obj]); + for (edges_size_type i = 0; i < g.num_edges(); ++i) + put(pmap, the_edges[i], values[i]); + } +} + +template struct graph_pickle_suite; +template struct graph_pickle_suite; + +} } } // end namespace boost::graph::python +