// graph-tool -- a general graph modification and manipulation thingy // // Copyright (C) 2006-2015 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 . #define NUMPY_EXPORT #include "numpy_bind.hh" #include "graph.hh" #include "graph_util.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 "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) { handle<> x(borrowed(obj_ptr)); object o(x); size_t N = len(o); for (size_t i = 0; i < N; ++i) { extract elem(o[i]); if (!elem.check()) return 0; } return obj_ptr; } static void construct(PyObject* obj_ptr, boost::python::converter::rvalue_from_python_stage1_data* data) { handle<> x(borrowed(obj_ptr)); object o(x); vector value; size_t N = len(o); for (size_t i = 0; i < N; ++i) value.push_back(extract(o[i])()); 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); } struct export_vector_types { template void operator()(ValueType) const { string type_name; if (std::is_same::value) type_name = "size_t"; else 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()); vc.def(vector_indexing_suite >()) .def("__eq__", &vector_equal_compare) .def("__ne__", &vector_nequal_compare); 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); } template void wrap_array(class_ >& vc, 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; PyObject* message = PyUnicode_FromString(e.what()); PyObject_SetAttrString(error, "message", message); 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; if (boost::python::len(o) < 2) throw ValueException("Invalid conversion to pair... Sequence is too short."); 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& g, string& name) const { using boost::python::detail::gcc_demangle; name = string(gcc_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; } // 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 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_sum(GraphInterface& gi, boost::any eprop, boost::any vprop); 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 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("gcc_demangle", &python::detail::gcc_demangle); def("graph_filtering_enabled", &graph_filtering_enabled); export_openmp(); boost::mpl::for_each::type>(export_vector_types()); boost::mpl::for_each>(export_vector_types()); class_("GraphInterface", init<>()) .def(init()) .def("GetNumberOfVertices", &GraphInterface::GetNumberOfVertices) .def("GetNumberOfEdges", &GraphInterface::GetNumberOfEdges) .def("SetDirected", &GraphInterface::SetDirected) .def("GetDirected", &GraphInterface::GetDirected) .def("SetReversed", &GraphInterface::SetReversed) .def("GetReversed", &GraphInterface::GetReversed) .def("SetKeepEpos", &GraphInterface::SetKeepEpos) .def("GetKeepEpos", &GraphInterface::GetKeepEpos) .def("SetVertexFilterProperty", &GraphInterface::SetVertexFilterProperty) .def("IsVertexFilterActive", &GraphInterface::IsVertexFilterActive) .def("SetEdgeFilterProperty", &GraphInterface::SetEdgeFilterProperty) .def("IsEdgeFilterActive", &GraphInterface::IsEdgeFilterActive) .def("PurgeVertices", &GraphInterface::PurgeVertices) .def("PurgeEdges", &GraphInterface::PurgeEdges) .def("ShiftVertexProperty", &GraphInterface::ShiftVertexProperty) .def("MoveVertexProperty", &GraphInterface::MoveVertexProperty) .def("ReIndexVertexProperty", &GraphInterface::ReIndexVertexProperty) .def("WriteToFile", &GraphInterface::WriteToFile) .def("ReadFromFile",&GraphInterface::ReadFromFile) .def("DegreeMap", &GraphInterface::DegreeMap) .def("Clear", &GraphInterface::Clear) .def("ClearEdges", &GraphInterface::ClearEdges) .def("GetVertexIndex", &GraphInterface::GetVertexIndex) .def("GetEdgeIndex", &GraphInterface::GetEdgeIndex) .def("GetMaxEdgeIndex", &GraphInterface::GetMaxEdgeIndex) .def("ReIndexEdges", &GraphInterface::ReIndexEdges) .def("GetGraphIndex", &GraphInterface::GetGraphIndex) .def("CopyVertexProperty", &GraphInterface::CopyVertexProperty) .def("CopyEdgeProperty", &GraphInterface::CopyEdgeProperty); 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(); #ifdef HAVE_SCIPY to_python_converter >(); to_python_converter >(); to_python_converter >(); to_python_converter >(); to_python_converter >(); #endif 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("infect_vertex_property", &infect_vertex_property); def("edge_endpoint", &edge_endpoint); def("out_edges_sum", &out_edges_sum); def("mark_edges", &mark_edges); def("perfect_ehash", &perfect_ehash); def("perfect_vhash", &perfect_vhash); 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); }