From cae5ff227016a576cede8f70688a6a1fb0e42625 Mon Sep 17 00:00:00 2001 From: Mansard <nmansard@laas.fr> Date: Fri, 11 Jul 2014 13:22:11 +0200 Subject: [PATCH] IVIGIT. --- CMakeLists.txt | 8 ++ python/{unittest.py => test_unit.py} | 20 ++++ src/eigenc.cpp | 132 ++++++++++++++++++++++++ src/eigentemplate.cpp | 144 +++++++++++++++++++++++++++ src/simple.cpp | 2 +- 5 files changed, 305 insertions(+), 1 deletion(-) rename python/{unittest.py => test_unit.py} (60%) create mode 100644 src/eigenc.cpp create mode 100644 src/eigentemplate.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b7e3c24c..e99613ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,9 @@ IF(NOT BOOST_NUMPY_DIR) ENDIF() FIND_PACKAGE(Boost 1.45.0) + IF(Boost_FOUND) + INCLUDE_DIRECTORIES("${Boost_INCLUDE_DIRS}" "/usr/include/python2.7" "/usr/include/eigen3" "Boost.NumPy") LINK_DIRECTORIES(${BOOST_NUMPY_DIR}) @@ -29,6 +31,12 @@ IF(Boost_FOUND) ADD_LIBRARY(eigen SHARED src/eigen.cpp) TARGET_LINK_LIBRARIES(eigen ${Boost_LIBRARIES} libboost_numpy.so) + ADD_LIBRARY(eigenc SHARED src/eigenc.cpp) + TARGET_LINK_LIBRARIES(eigenc ${Boost_LIBRARIES}) + + ADD_LIBRARY(eigentemplate SHARED src/eigentemplate.cpp) + TARGET_LINK_LIBRARIES(eigentemplate ${Boost_LIBRARIES}) + ELSEIF(NOT Boost_FOUND) MESSAGE(FATAL_ERROR "Unable to find correct Boost version. Did you set BOOST_ROOT?") ENDIF() diff --git a/python/unittest.py b/python/test_unit.py similarity index 60% rename from python/unittest.py rename to python/test_unit.py index c27daac8..143c8e71 100644 --- a/python/unittest.py +++ b/python/test_unit.py @@ -2,6 +2,7 @@ import numpy as np +''' import libsimple print libsimple.char() @@ -28,6 +29,25 @@ print "matrix ===> " libeigen.test2(a) print "array ===> " libeigen.test2(b) +''' + +import libeigentemplate +print "===> From C++ to Py" +print libeigentemplate.test() +print "===> From Vec C++ to Py" +print libeigentemplate.testVec() +print "===> From Py to C++" +a = np.random.random([5,5]) +for i in range(5): + for j in range(5): + a[i,j] = i*5+j +#a = np.random.random([ +print a +libeigentemplate.test2(a) +print "===> From Py::slice to C++" +b=a[1:5,1:3] +print b +libeigentemplate.test2(b) diff --git a/src/eigenc.cpp b/src/eigenc.cpp new file mode 100644 index 00000000..73620e64 --- /dev/null +++ b/src/eigenc.cpp @@ -0,0 +1,132 @@ +#include <Eigen/Core> +#include <boost/python.hpp> +#include <numpy/arrayobject.h> + +namespace boopy +{ + namespace bp = boost::python; + + template <typename SCALAR> struct NumpyEquivalentType {}; + template <> struct NumpyEquivalentType<double> { enum { type_code = NPY_DOUBLE };}; + template <> struct NumpyEquivalentType<int> { enum { type_code = NPY_INT };}; + template <> struct NumpyEquivalentType<float> { enum { type_code = NPY_FLOAT };}; + + struct EigenMatrix_to_python_matrix + { + static PyObject* convert(Eigen::MatrixXd const& mat) + { + typedef Eigen::MatrixXd::Scalar T; + + npy_intp shape[2] = { mat.rows(), mat.cols() }; + PyArrayObject* pyArray = (PyArrayObject*) + PyArray_SimpleNew(2, shape, + NumpyEquivalentType<T>::type_code); + + T* pyData = (T*)PyArray_DATA(pyArray); + for(int i=0;i<mat.rows();++i) + for(int j=0;j<mat.cols();++j) + pyData[i*mat.cols()+j] = mat(i,j); + + return ((PyObject*)pyArray); + } + }; + + + struct EigenMatrix_from_python_array + { + + EigenMatrix_from_python_array() + { + bp::converter::registry + ::push_back(&convertible, + &construct, + bp::type_id<Eigen::MatrixXd>()); + } + + // Determine if obj_ptr can be converted in a Eigenvec + static void* convertible(PyObject* obj_ptr) + { + typedef Eigen::MatrixXd::Scalar T; + + if (!PyArray_Check(obj_ptr)) { + return 0; + } + if (PyArray_NDIM(obj_ptr) > 2) { + return 0; + } + if (PyArray_ObjectType(obj_ptr, 0) != NumpyEquivalentType<T>::type_code) { + return 0; + } + int flags = PyArray_FLAGS(obj_ptr); + if (!(flags & NPY_C_CONTIGUOUS)) { + return 0; + } + if (!(flags & NPY_ALIGNED)) { + return 0; + } + + return obj_ptr; + } + + // Convert obj_ptr into a Eigenvec + static void construct(PyObject* pyObj, + bp::converter::rvalue_from_python_stage1_data* memory) + { + typedef Eigen::MatrixXd::Scalar T; + using namespace Eigen; + + PyArrayObject * pyArray = reinterpret_cast<PyArrayObject*>(pyObj); + int ndims = PyArray_NDIM(pyArray); + assert(ndims == 2); + + int dtype_size = (PyArray_DESCR(pyArray))->elsize; + int s1 = PyArray_STRIDE(pyArray, 0); + assert(s1 % dtype_size == 0); + + int R = MatrixXd::RowsAtCompileTime; + int C = MatrixXd::ColsAtCompileTime; + if (R == Eigen::Dynamic) R = PyArray_DIMS(pyArray)[0]; + else assert(PyArray_DIMS(pyArray)[0]==R); + + if (C == Eigen::Dynamic) C = PyArray_DIMS(pyArray)[1]; + else assert(PyArray_DIMS(pyArray)[1]==C); + + T* pyData = reinterpret_cast<T*>(PyArray_DATA(pyArray)); + + void* storage = ((bp::converter::rvalue_from_python_storage<MatrixXd>*) + (memory))->storage.bytes; + MatrixXd & mat = * new (storage) MatrixXd(R,C); + for(int i=0;i<R;++i) + for(int j=0;j<C;++j) + mat(i,j) = pyData[i*C+j]; + + memory->convertible = storage; + } + }; + + +} + +Eigen::MatrixXd test() +{ + Eigen::MatrixXd mat = Eigen::MatrixXd::Random(5,5); + std::cout << "EigenMAt = " << mat << std::endl; + return mat; +} + +void test2( Eigen::MatrixXd mat ) +{ + std::cout << "test2: dim = " << mat.rows() << " ||| m[0,0] = " << mat(0,0) << std::endl; +} + +BOOST_PYTHON_MODULE(libeigenc) +{ + import_array(); + namespace bp = boost::python; + bp::to_python_converter<Eigen::MatrixXd, + boopy::EigenMatrix_to_python_matrix>(); + boopy::EigenMatrix_from_python_array(); + + bp::def("test", test); + bp::def("test2", test2); +} diff --git a/src/eigentemplate.cpp b/src/eigentemplate.cpp new file mode 100644 index 00000000..76072b29 --- /dev/null +++ b/src/eigentemplate.cpp @@ -0,0 +1,144 @@ +#include <Eigen/Core> +#include <boost/python.hpp> +#include <numpy/arrayobject.h> + +namespace boopy +{ + namespace bp = boost::python; + + template <typename SCALAR> struct NumpyEquivalentType {}; + template <> struct NumpyEquivalentType<double> { enum { type_code = NPY_DOUBLE };}; + template <> struct NumpyEquivalentType<int> { enum { type_code = NPY_INT };}; + template <> struct NumpyEquivalentType<float> { enum { type_code = NPY_FLOAT };}; + + /* --- TO PYTHON -------------------------------------------------------------- */ + template< typename MatType > + struct EigenMatrix_to_python_matrix + { + static PyObject* convert(MatType const& mat) + { + typedef typename MatType::Scalar T; + const int R = mat.rows(), C = mat.cols(); + + npy_intp shape[2] = { R,C }; + PyArrayObject* pyArray = (PyArrayObject*) + PyArray_SimpleNew(2, shape, + NumpyEquivalentType<T>::type_code); + + T* pyData = (T*)PyArray_DATA(pyArray); + Eigen::Map< Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic,Eigen::RowMajor> > pyMatrix(pyData,R,C); + pyMatrix = mat; + + return (PyObject*)pyArray; + } + }; + + /* --- FROM PYTHON ------------------------------------------------------------ */ + struct EigenMatrix_from_python_array + { + + EigenMatrix_from_python_array() + { + bp::converter::registry + ::push_back(&convertible, + &construct, + bp::type_id<Eigen::MatrixXd>()); + } + + // Determine if obj_ptr can be converted in a Eigenvec + static void* convertible(PyObject* obj_ptr) + { + typedef Eigen::MatrixXd MatType; + typedef MatType::Scalar T; + + if (!PyArray_Check(obj_ptr)) return 0; + + if (PyArray_NDIM(obj_ptr) != 2) + if ( (PyArray_NDIM(obj_ptr) !=1) || (MatType::IsVectorAtCompileTime) ) + return 0; + + if (PyArray_ObjectType(obj_ptr, 0) != NumpyEquivalentType<T>::type_code) + return 0; + + if (!(PyArray_FLAGS(obj_ptr) & NPY_ALIGNED)) + { + std::cerr << "NPY non-aligned matrices are not implemented." << std::endl; + return 0; + } + + return obj_ptr; + } + + // Convert obj_ptr into a Eigenvec + static void construct(PyObject* pyObj, + bp::converter::rvalue_from_python_stage1_data* memory) + { + typedef Eigen::MatrixXd MatType; + typedef MatType::Scalar T; + using namespace Eigen; + + PyArrayObject * pyArray = reinterpret_cast<PyArrayObject*>(pyObj); + int ndims = PyArray_NDIM(pyArray); + assert(ndims == 2); // TODO: handle vectors + + int itemsize = PyArray_ITEMSIZE(pyArray); + int stride1 = PyArray_STRIDE(pyArray, 0) / itemsize; + int stride2 = PyArray_STRIDE(pyArray, 1) / itemsize; + std::cout << "STRIDE = " << stride1 << " x " << stride2 << std::endl; + + int R = MatrixXd::RowsAtCompileTime; + int C = MatrixXd::ColsAtCompileTime; + if (R == Eigen::Dynamic) R = PyArray_DIMS(pyArray)[0]; + else assert(PyArray_DIMS(pyArray)[0]==R); + + if (C == Eigen::Dynamic) C = PyArray_DIMS(pyArray)[1]; + else assert(PyArray_DIMS(pyArray)[1]==C); + + T* pyData = reinterpret_cast<T*>(PyArray_DATA(pyArray)); + + void* storage = ((bp::converter::rvalue_from_python_storage<MatrixXd>*) + (memory))->storage.bytes; + MatrixXd & mat = * new (storage) MatrixXd(R,C); + for(int i=0;i<R;++i) + for(int j=0;j<C;++j) + mat(i,j) = pyData[i*C+j]; + + memory->convertible = storage; + } + }; + + +} + +Eigen::MatrixXd test() +{ + Eigen::MatrixXd mat = Eigen::MatrixXd::Random(3,6); + std::cout << "EigenMAt = " << mat << std::endl; + return mat; +} +Eigen::VectorXd testVec() +{ + Eigen::VectorXd mat = Eigen::VectorXd::Random(6); + std::cout << "EigenVec = " << mat << std::endl; + return mat; +} + +void test2( Eigen::MatrixXd mat ) +{ + std::cout << mat << std::endl; +} + +BOOST_PYTHON_MODULE(libeigentemplate) +{ + import_array(); + namespace bp = boost::python; + bp::to_python_converter<Eigen::MatrixXd, + boopy::EigenMatrix_to_python_matrix<Eigen::MatrixXd> >(); + bp::to_python_converter<Eigen::VectorXd, + boopy::EigenMatrix_to_python_matrix<Eigen::VectorXd> >(); + boopy::EigenMatrix_from_python_array(); + + bp::def("test", test); + bp::def("testVec", testVec); + bp::def("test2", test2); +} diff --git a/src/simple.cpp b/src/simple.cpp index 85b631a3..558e77b9 100644 --- a/src/simple.cpp +++ b/src/simple.cpp @@ -20,7 +20,7 @@ std::string teststr() Eigen::VectorXd testeigenvec() { - Eigen::VectorXd v(5); + Eigen::VectorXd v(555); return v; } -- GitLab