c++ arrays numpy boost-python

c++ - cómo devolver numpy.array desde boost:: python?



arrays boost-python (5)

Es un poco tarde, pero después de muchos intentos infructuosos encontré una forma de exponer matrices c ++ como matrices numpy directamente. Aquí hay un breve ejemplo de C ++ 11 usando boost::python y Eigen:

#include <numpy/ndarrayobject.h> #include <boost/python.hpp> #include <Eigen/Core> // c++ type struct my_type { Eigen::Vector3d position; }; // wrap c++ array as numpy array static boost::python::object wrap(double* data, npy_intp size) { using namespace boost::python; npy_intp shape[1] = { size }; // array size PyObject* obj = PyArray_New(&PyArray_Type, 1, shape, NPY_DOUBLE, // data type NULL, data, // data pointer 0, NPY_ARRAY_CARRAY, // NPY_ARRAY_CARRAY_RO for readonly NULL); handle<> array( obj ); return object(array); } // module definition BOOST_PYTHON_MODULE(test) { // numpy requires this import_array(); using namespace boost::python; // wrapper for my_type class_< my_type >("my_type") .add_property("position", +[](my_type& self) -> object { return wrap(self.position.data(), self.position.size()); }); }

El ejemplo describe un "captador" para la propiedad. Para el "setter", la forma más fácil es asignar los elementos de la matriz manualmente desde un objeto boost::python::object utilizando un boost::python::stl_input_iterator<double> .

Me gustaría devolver algunos datos del código c ++ como un objeto numpy.array . Eché un vistazo a boost::python::numeric , pero su documentación es muy escueta. ¿Puedo obtener un ejemplo de, por ejemplo, devolver un vector<double> (no muy grande) vector<double> a python? No me importa hacer copias de datos.


Hacerlo usando la API numpy directamente no es necesariamente difícil, pero utilizo boost :: multiarray regularmente para mis proyectos y encuentro conveniente transferir las formas de la matriz entre el límite C ++ / Python automáticamente. Entonces, aquí está mi receta. Utilice http://code.google.com/p/numpy-boost/ , o mejor aún, this versión del encabezado numpy_boost.hpp; que es una mejor opción para los proyectos boost :: python de archivos múltiples, aunque usa algo de C ++ 11. Luego, desde su código boost :: python, use algo como esto:

PyObject* myfunc(/*....*/) { // If your data is already in a boost::multiarray object: // numpy_boost< double, 1 > to_python( numpy_from_boost_array(result_cm) ); // otherwise: numpy_boost< double, 1> to_python( boost::extents[n] ); std::copy( my_vector.begin(), my_vector.end(), to_python.begin() ); PyObject* result = to_python.py_ptr(); Py_INCREF( result ); return result; }


Miré las respuestas disponibles y pensé, "esto será fácil". Procedí a pasar horas intentando lo que parecían ejemplos / adaptaciones triviales de las respuestas.

Luego implementé la respuesta de @maxim exactamente (tuve que instalar Eigen) y funcionó bien, pero todavía tuve problemas para adaptarlo. Mis problemas eran mayormente (por números) tontos, errores de sintaxis, pero adicionalmente estaba usando un puntero a los datos de una copia de std :: vector después de que el vector pareciera ser eliminado de la pila.

En este ejemplo, se devuelve un puntero al std :: vector, pero también se puede devolver el tamaño y el puntero de datos () o utilizar cualquier otra implementación que proporcione a su arreglo numpy acceso a los datos subyacentes de una manera estable (es decir, garantizada existe):

class_<test_wrap>("test_wrap") .add_property("values", +[](test_wrap& self) -> object { return wrap(self.pvalues()->data(),self.pvalues()->size()); }) ;

Para test_wrap con std::vector<double> (normalmente, pvalues ​​() puede devolver el puntero sin rellenar el vector):

class test_wrap { public: std::vector<double> mValues; std::vector<double>* pvalues() { mValues.clear(); for(double d_ = 0.0; d_ < 4; d_+=0.3) { mValues.push_back(d_); } return &mValues; } };

El ejemplo completo está en Github, por lo que puede omitir los tediosos pasos de transcripción y preocuparse menos por la compilación, las librerías, etc. Debería poder hacer lo siguiente y obtener un ejemplo de funcionamiento (si tiene las funciones necesarias instaladas y la configuración de la ruta) ya):

git clone https://github.com/ransage/boost_numpy_example.git cd boost_numpy_example # Install virtualenv, numpy if necessary; update path (see below*) cd build && cmake .. && make && ./test_np.py

Esto debería dar el resultado:

# cmake/make output values has type <type ''numpy.ndarray''> values has len 14 values is [ 0. 0.3 0.6 0.9 1.2 1.5 1.8 2.1 2.4 2.7 3. 3.3 3.6 3.9]

* En mi caso, puse numpy en un virtualenv de la siguiente manera: esto debería ser innecesario si puedes ejecutar python -c "import numpy; print numpy.get_include()" como lo sugiere @max:

# virtualenv, pip, path unnecessary if your Python has numpy virtualenv venv ./venv/bin/pip install -r requirements.txt export PATH="$(pwd)/venv/bin:$PATH"

¡Que te diviertas! :-)


Otra interfaz entre Boost.Python y NumPy se puede encontrar aquí:

https://github.com/ndarray/Boost.NumPy

Es una envoltura moderadamente completa de NumPy C-API en una interfaz Boost.Python, con la intención de eventualmente enviarla a Boost. No estoy seguro de que la documentación sea en general mejor que boost :: python :: numérica en este momento, pero hay muchos ejemplos de código y, al menos, está en desarrollo activo. Es bastante bajo nivel, y principalmente se enfoca en cómo abordar el problema más difícil de cómo pasar datos de C ++ hacia y desde NumPy sin copiar, pero así es como harías un retorno estándar de copiado con eso:

#include "boost/numpy.hpp" namespace bp = boost::python; namespace bn = boost::numpy; std::vector<double> myfunc(...); bn::ndarray mywrapper(...) { std::vector<double> v = myfunc(...); Py_intptr_t shape[1] = { v.size() }; bn::ndarray result = bn::zeros(1, shape, bn::dtype::get_builtin<double>()); std::copy(v.begin(), v.end(), reinterpret_cast<double*>(result.get_data())); return result; } BOOST_PYTHON_MODULE(example) { bn::initialize(); bp::def("myfunc", mywrapper); }


Una solución que no requiere que descargue ninguna biblioteca especial de C ++ de terceros (pero necesita un numpy).

#include <numpy/ndarrayobject.h> // ensure you include this header boost::python::object stdVecToNumpyArray( std::vector<double> const& vec ) { npy_intp size = vec.size(); /* const_cast is rather horrible but we need a writable pointer in C++11, vec.data() will do the trick but you will still need to const_cast */ double * data = size ? const_cast<double *>(&vec[0]) : static_cast<double *>(NULL); // create a PyObject * from pointer and data PyObject * pyObj = PyArray_SimpleNewFromData( 1, &size, NPY_DOUBLE, data ); boost::python::handle<> handle( pyObj ); boost::python::numeric::array arr( handle ); /* The problem of returning arr is twofold: firstly the user can modify the data which will betray the const-correctness Secondly the lifetime of the data is managed by the C++ API and not the lifetime of the numpy array whatsoever. But we have a simple solution.. */ return arr.copy(); // copy the object. numpy owns the copy now. }

Por supuesto, puede escribir una función de doble * y tamaño, que es genérica y luego invocarla desde el vector extrayendo esta información. También podría escribir una plantilla, pero necesitaría algún tipo de mapeo desde el tipo de datos hasta la enumeración NPY_TYPES .