// graph-tool -- a general graph modification and manipulation thingy // // Copyright (C) 2006-2017 Tiago de Paula Peixoto // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 3 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include "graph.hh" #include "graph_util.hh" #define NUMPY_EXPORT #include "numpy_bind.hh" #include "graph_python_interface.hh" #include "random.hh" #ifdef HAVE_SCIPY // integration with scipy weave #include "weave/scxx/object.h" #include "weave/scxx/list.h" #include "weave/scxx/tuple.h" #include "weave/scxx/dict.h" #include "weave/scxx/str.h" #endif #include #include #include #include "bytesobject.h" using namespace std; using namespace graph_tool; using namespace boost; using namespace boost::python; struct LibInfo { string GetName() const {return PACKAGE_NAME;} string GetAuthor() const {return AUTHOR;} string GetCopyright() const {return COPYRIGHT;} string GetVersion() const {return VERSION " (commit " GIT_COMMIT ", " GIT_COMMIT_DATE ")";} string GetLicense() const {return "GPL version 3 or above";} string GetCXXFLAGS() const {return CPPFLAGS " " CXXFLAGS " " LDFLAGS;} string GetInstallPrefix() const {return INSTALL_PREFIX;} string GetPythonDir() const {return PYTHON_DIR;} string GetGCCVersion() const { stringstream s; s << __GNUC__ << "." << __GNUC_MINOR__ << "." << __GNUC_PATCHLEVEL__; return s.str(); } }; template struct vector_from_list { vector_from_list() { boost::python::converter::registry::push_back (&convertible, &construct, boost::python::type_id >()); } static void* convertible(PyObject* obj_ptr) { // can't verify without potentially exhausting an iterator return obj_ptr; } static void construct(PyObject* obj_ptr, boost::python::converter::rvalue_from_python_stage1_data* data) { python::handle<> x(python::borrowed(obj_ptr)); python::object o(x); vector value; python::stl_input_iterator iter(o), end; for (; iter != end; ++iter) value.push_back(*iter); void* storage = ( (boost::python::converter::rvalue_from_python_storage >*) data)->storage.bytes; new (storage) vector(value); data->convertible = storage; } }; template bool vector_equal_compare(const vector& v1, const vector& v2) { if (v1.size() != v2.size()) return false; for (size_t i = 0; i < v1.size(); ++i) { if (v1[i] != v2[i]) return false; } return true; } template bool vector_nequal_compare(const vector& v1, const vector& v2) { return !vector_equal_compare(v1,v2); } template python::object get_vector_state(std::vector& v) { return wrap_vector_owned(v); } template void set_vector_state(std::vector& v, python::object state) { auto a = get_array(state); v.clear(); v.reserve(a.size()); v.insert(v.end(), a.begin(), a.end()); } struct export_vector_types { template void operator()(ValueType, std::string type_name = "") const { if (type_name.empty()) type_name = get_type_name<>()(typeid(ValueType)); std::replace(type_name.begin(), type_name.end(), ' ', '_'); string name = "Vector_" + type_name; class_ > vc(name.c_str()); std::function&)> hasher = [] (const vector& v) -> size_t { return std::hash>()(v); }; std::function&, size_t size)> resize = [] (vector& v, size_t n) { v.resize(n); }; std::function&, size_t n)> reserve = [] (vector& v, size_t n) { v.reserve(n); }; std::function&)> shrink_to_fit = [] (vector& v) { v.shrink_to_fit(); }; std::function&)> empty = [] (vector& v) -> bool { return v.empty(); }; std::function&)> clear = [] (vector& v) { v.clear(); }; vc.def(vector_indexing_suite >()) .def("__eq__", &vector_equal_compare) .def("__ne__", &vector_nequal_compare) .def("__hash__", hasher) .def("resize", resize) .def("shrink_to_fit", shrink_to_fit) .def("clear", clear) .def("empty", empty); wrap_array(vc, typename boost::mpl::has_key::type()); vector_from_list(); } template void wrap_array(class_ >& vc, boost::mpl::true_) const { vc.def("get_array", &wrap_vector_not_owned) .def("__getstate__", &get_vector_state) .def("__setstate__", &set_vector_state) .enable_pickling(); } template void wrap_array(class_ >&, boost::mpl::false_) const { } }; // exception translation template void graph_exception_translator(const Exception& e) { PyObject* error; if (std::is_same::value) error = PyExc_RuntimeError; if (std::is_same::value) error = PyExc_IOError; if (std::is_same::value) error = PyExc_ValueError; PyErr_SetString(error, e.what()); } void raise_error(const string& msg) { throw GraphException(msg); } template struct pair_to_tuple { static PyObject* convert(const pair& p) { boost::python::tuple t = boost::python::make_tuple(p.first,p.second); return incref(t.ptr()); } }; template struct pair_from_tuple { pair_from_tuple() { converter::registry::push_back(&convertible, &construct, boost::python::type_id >()); } static void* convertible(PyObject* obj_ptr) { handle<> x(borrowed(obj_ptr)); object o(x); if (boost::python::len(o) < 2) return 0; extract first(o[0]); extract second(o[1]); if (!first.check() || !second.check()) return 0; return obj_ptr; } static void construct(PyObject* obj_ptr, converter::rvalue_from_python_stage1_data* data) { handle<> x(borrowed(obj_ptr)); object o(x); pair value; value.first = extract(o[0])(); value.second = extract(o[1])(); void* storage = ( (boost::python::converter::rvalue_from_python_storage >*) data)->storage.bytes; new (storage) pair(value); data->convertible = storage; } }; template struct variant_from_python { variant_from_python() { converter::registry::push_back (&convertible, &construct, boost::python::type_id()); } static void* convertible(PyObject* obj_ptr) { handle<> x(borrowed(obj_ptr)); object o(x); extract str(o); if (!str.check()) return 0; return obj_ptr; } static void construct(PyObject* obj_ptr, converter::rvalue_from_python_stage1_data* data) { handle<> x(borrowed(obj_ptr)); object o(x); ValueType value = extract(o)(); GraphInterface::deg_t deg = value; void* storage = ( (boost::python::converter::rvalue_from_python_storage *) data)->storage.bytes; new (storage) GraphInterface::deg_t(deg); data->convertible = storage; } }; // scipy weave integration #ifdef HAVE_SCIPY template struct scxx_to_python { static PyObject* convert(const ScxxType& o) { return incref((PyObject*)(o)); } }; #endif template struct integer_from_convertible { integer_from_convertible() { converter::registry::push_back(&convertible, &construct, boost::python::type_id()); } static void* convertible(PyObject* obj_ptr) { if (PyObject_HasAttrString(obj_ptr, "__int__")) return obj_ptr; return 0; } static void construct(PyObject* obj_ptr, converter::rvalue_from_python_stage1_data* data) { handle<> x(borrowed(obj_ptr)); object o(x); T value = extract(o.attr("__int__")())(); void* storage = ( (boost::python::converter::rvalue_from_python_storage*) data)->storage.bytes; new (storage) T(value); data->convertible = storage; } }; template struct float_from_convertible { float_from_convertible() { converter::registry::push_back(&convertible, &construct, boost::python::type_id()); } static void* convertible(PyObject* obj_ptr) { if (PyObject_HasAttrString(obj_ptr, "__float__")) return obj_ptr; return 0; } static void construct(PyObject* obj_ptr, converter::rvalue_from_python_stage1_data* data) { handle<> x(borrowed(obj_ptr)); object o(x); T value = extract(o.attr("__float__")())(); void* storage = ( (boost::python::converter::rvalue_from_python_storage*) data)->storage.bytes; new (storage) T(value); data->convertible = storage; } }; // persistent python object IO namespace graph_tool { extern python::object object_pickler; extern python::object object_unpickler; } void set_pickler(boost::python::object o) { graph_tool::object_pickler = o; } void set_unpickler(boost::python::object o) { graph_tool::object_unpickler = o; } boost::python::list get_property_types() { boost::python::list plist; for (int i = 0; i < boost::mpl::size::value; ++i) plist.append(string(type_names[i])); return plist; } struct graph_type_name { typedef void result_type; template void operator()(const Graph&, string& name) const { name = name_demangle(typeid(Graph).name()); } }; string get_graph_type(GraphInterface& g) { string name; run_action<>()(g, std::bind(graph_type_name(), std::placeholders::_1, std::ref(name)))(); return name; } size_t get_ptr(std::shared_ptr& p) { return size_t(p.get()); }; // numpy array interface weirdness void* do_import_array() { import_array1(NULL); return NULL; } void ungroup_vector_property(GraphInterface& g, boost::any vector_prop, boost::any prop, size_t pos, bool edge); void group_vector_property(GraphInterface& g, boost::any vector_prop, boost::any prop, size_t pos, bool edge); void property_map_values(GraphInterface& g, boost::any src_prop, boost::any tgt_prop, boost::python::object mapper, bool edge); void infect_vertex_property(GraphInterface& gi, boost::any prop, boost::python::object val); void edge_endpoint(GraphInterface& gi, boost::any prop, boost::any eprop, std::string endpoint); void out_edges_op(GraphInterface& gi, boost::any eprop, boost::any vprop, std::string op); void mark_edges(GraphInterface& gi, boost::any prop); void perfect_ehash(GraphInterface& gi, boost::any prop, boost::any hprop, boost::any& dict); void perfect_vhash(GraphInterface& gi, boost::any prop, boost::any hprop, boost::any& dict); void set_vertex_property(GraphInterface& gi, boost::any prop, boost::python::object val); void set_edge_property(GraphInterface& gi, boost::any prop, boost::python::object val); void export_python_interface(); void export_openmp(); BOOST_PYTHON_MODULE(libgraph_tool_core) { using namespace boost::python; // numpy do_import_array(); export_python_interface(); // random numbers class_("rng_t"); def("get_rng", get_rng); register_exception_translator (graph_exception_translator); register_exception_translator (graph_exception_translator); register_exception_translator (graph_exception_translator); def("raise_error", &raise_error); def("get_property_types", &get_property_types); class_("any") .def("empty", &boost::any::empty) .def("type", &boost::any::type, return_value_policy()); class_("type_info", no_init) .def("name", &std::type_info::name) .def("hash_code", &std::type_info::hash_code); def("name_demangle", &name_demangle); def("graph_filtering_enabled", &graph_filtering_enabled); export_openmp(); boost::mpl::for_each::type>(export_vector_types()); export_vector_types()(size_t(), "size_t"); export_vector_types()(std::vector(), "Vector_double"); class_("GraphInterface", init<>()) .def(init()) .def("get_num_vertices", &GraphInterface::get_num_vertices) .def("get_num_edges", &GraphInterface::get_num_edges) .def("set_directed", &GraphInterface::set_directed) .def("get_directed", &GraphInterface::get_directed) .def("set_reversed", &GraphInterface::set_reversed) .def("get_reversed", &GraphInterface::get_reversed) .def("set_keep_epos", &GraphInterface::set_keep_epos) .def("get_keep_epos", &GraphInterface::get_keep_epos) .def("set_vertex_filter_property", &GraphInterface::set_vertex_filter_property) .def("is_vertex_filter_active", &GraphInterface::is_vertex_filter_active) .def("set_edge_filter_property", &GraphInterface::set_edge_filter_property) .def("is_edge_filter_active", &GraphInterface::is_edge_filter_active) .def("purge_vertices", &GraphInterface::purge_vertices) .def("purge_edges", &GraphInterface::purge_edges) .def("shift_vertex_property", &GraphInterface::shift_vertex_property) .def("move_vertex_property", &GraphInterface::move_vertex_property) .def("re_index_vertex_property", &GraphInterface::re_index_vertex_property) .def("write_to_file", &GraphInterface::write_to_file) .def("read_from_file",&GraphInterface::read_from_file) .def("degree_map", &GraphInterface::degree_map) .def("clear", &GraphInterface::clear) .def("clear_edges", &GraphInterface::clear_edges) .def("get_vertex_index", &GraphInterface::get_vertex_index) .def("get_edge_index", &GraphInterface::get_edge_index) .def("get_edge_index_range", &GraphInterface::get_edge_index_range) .def("re_index_edges", &GraphInterface::re_index_edges) .def("shrink_to_fit", &GraphInterface::shrink_to_fit) .def("get_graph_index", &GraphInterface::get_graph_index) .def("copy_vertex_property", &GraphInterface::copy_vertex_property) .def("copy_edge_property", &GraphInterface::copy_edge_property) .def("get_graph_ptr", &GraphInterface::get_graph_ptr) .def("get_graph_view", &GraphInterface::get_graph_view); class_("vertex_index_map", no_init); class_("edge_index_map", no_init); class_("graph_index_map", no_init); enum_("Degree") .value("In", GraphInterface::IN_DEGREE) .value("Out", GraphInterface::OUT_DEGREE) .value("Total", GraphInterface::TOTAL_DEGREE); variant_from_python(); variant_from_python(); to_python_converter, pair_to_tuple >(); to_python_converter, pair_to_tuple >(); to_python_converter, pair_to_tuple >(); pair_from_tuple(); pair_from_tuple(); integer_from_convertible(); integer_from_convertible(); integer_from_convertible(); integer_from_convertible(); integer_from_convertible(); integer_from_convertible(); integer_from_convertible(); float_from_convertible(); float_from_convertible(); float_from_convertible(); class_> ("shared_ptr", no_init) .def("get", &get_ptr); class_("IStream", no_init).def("read", &IStream::read); class_("OStream", no_init).def("write", &OStream::write). def("flush", &OStream::flush); def("set_pickler", &set_pickler); def("set_unpickler", &set_unpickler); def("group_vector_property", &group_vector_property); def("ungroup_vector_property", &ungroup_vector_property); def("property_map_values", &property_map_values); def("infect_vertex_property", &infect_vertex_property); def("edge_endpoint", &edge_endpoint); def("out_edges_op", &out_edges_op); def("mark_edges", &mark_edges); def("perfect_ehash", &perfect_ehash); def("perfect_vhash", &perfect_vhash); def("set_vertex_property", &set_vertex_property); def("set_edge_property", &set_edge_property); class_("mod_info") .add_property("name", &LibInfo::GetName) .add_property("author", &LibInfo::GetAuthor) .add_property("copyright", &LibInfo::GetCopyright) .add_property("version", &LibInfo::GetVersion) .add_property("license", &LibInfo::GetLicense) .add_property("cxxflags", &LibInfo::GetCXXFLAGS) .add_property("install_prefix", &LibInfo::GetInstallPrefix) .add_property("python_dir", &LibInfo::GetPythonDir) .add_property("gcc_version", &LibInfo::GetGCCVersion); def("get_graph_type", &get_graph_type); }