diff --git a/CMakeLists.txt b/CMakeLists.txt index 43d10098f3d446202f12221184bd1d5c70722c33..d65f9d58d61537465e07df578644411cc9f12502 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -102,20 +102,25 @@ SET(${PROJECT_NAME}_HEADERS include/eigenpy/computation-info.hpp include/eigenpy/eigenpy.hpp include/eigenpy/exception.hpp + include/eigenpy/scalar-conversion.hpp include/eigenpy/expose.hpp include/eigenpy/details.hpp include/eigenpy/fwd.hpp + include/eigenpy/eigen-allocator.hpp + include/eigenpy/eigen-to-python.hpp + include/eigenpy/eigen-from-python.hpp include/eigenpy/map.hpp include/eigenpy/geometry.hpp include/eigenpy/geometry-conversion.hpp include/eigenpy/memory.hpp + include/eigenpy/numpy.hpp + include/eigenpy/numpy-allocator.hpp include/eigenpy/numpy-type.hpp include/eigenpy/registration.hpp include/eigenpy/angle-axis.hpp include/eigenpy/quaternion.hpp include/eigenpy/stride.hpp include/eigenpy/ref.hpp - include/eigenpy/details/rvalue_from_python_data.hpp include/eigenpy/version.hpp ) @@ -141,6 +146,7 @@ SET(${PROJECT_NAME}_SOURCES ${${PROJECT_NAME}_DECOMPOSITIONS_SOURCES} src/exception.cpp src/eigenpy.cpp + src/numpy.cpp src/matrix-float.cpp src/matrix-complex-float.cpp src/matrix-complex-double.cpp diff --git a/include/eigenpy/details.hpp b/include/eigenpy/details.hpp index 9a43df4b7389fbf22ada994408c50a8f3f858fe6..65d23619a898ccc4d9a5c53bc72b2998e47c5f0d 100644 --- a/include/eigenpy/details.hpp +++ b/include/eigenpy/details.hpp @@ -6,20 +6,20 @@ #ifndef __eigenpy_details_hpp__ #define __eigenpy_details_hpp__ -#include "eigenpy/details/rvalue_from_python_data.hpp" #include "eigenpy/fwd.hpp" - -#include <patchlevel.h> // For PY_MAJOR_VERSION -#include <iostream> - #include "eigenpy/eigenpy.hpp" + #include "eigenpy/numpy-type.hpp" +#include "eigenpy/scalar-conversion.hpp" + +#include "eigenpy/eigen-allocator.hpp" +#include "eigenpy/eigen-to-python.hpp" +#include "eigenpy/eigen-from-python.hpp" + #include "eigenpy/registration.hpp" #include "eigenpy/map.hpp" #include "eigenpy/exception.hpp" -#define GET_PY_ARRAY_TYPE(array) PyArray_ObjectType(reinterpret_cast<PyObject *>(array), 0) - namespace boost { namespace python { namespace detail { template<class MatType> @@ -54,541 +54,19 @@ namespace boost { namespace python { namespace detail { namespace eigenpy { - template <typename SCALAR> struct NumpyEquivalentType {}; - template <> struct NumpyEquivalentType<float> { enum { type_code = NPY_FLOAT };}; - template <> struct NumpyEquivalentType< std::complex<float> > { enum { type_code = NPY_CFLOAT };}; - template <> struct NumpyEquivalentType<double> { enum { type_code = NPY_DOUBLE };}; - template <> struct NumpyEquivalentType< std::complex<double> > { enum { type_code = NPY_CDOUBLE };}; - template <> struct NumpyEquivalentType<long double> { enum { type_code = NPY_LONGDOUBLE };}; - template <> struct NumpyEquivalentType< std::complex<long double> > { enum { type_code = NPY_CLONGDOUBLE };}; - template <> struct NumpyEquivalentType<int> { enum { type_code = NPY_INT };}; - template <> struct NumpyEquivalentType<long> { enum { type_code = NPY_LONG };}; - - template <typename SCALAR1, typename SCALAR2> - struct FromTypeToType : public boost::false_type {}; - - template <typename SCALAR> - struct FromTypeToType<SCALAR,SCALAR> : public boost::true_type {}; - - template <> struct FromTypeToType<int,long> : public boost::true_type {}; - template <> struct FromTypeToType<int,float> : public boost::true_type {}; - template <> struct FromTypeToType<int,std::complex<float> > : public boost::true_type {}; - template <> struct FromTypeToType<int,double> : public boost::true_type {}; - template <> struct FromTypeToType<int,std::complex<double> > : public boost::true_type {}; - template <> struct FromTypeToType<int,long double> : public boost::true_type {}; - template <> struct FromTypeToType<int,std::complex<long double> > : public boost::true_type {}; - - template <> struct FromTypeToType<long,float> : public boost::true_type {}; - template <> struct FromTypeToType<long,std::complex<float> > : public boost::true_type {}; - template <> struct FromTypeToType<long,double> : public boost::true_type {}; - template <> struct FromTypeToType<long,std::complex<double> > : public boost::true_type {}; - template <> struct FromTypeToType<long,long double> : public boost::true_type {}; - template <> struct FromTypeToType<long,std::complex<long double> > : public boost::true_type {}; - - template <> struct FromTypeToType<float,std::complex<float> > : public boost::true_type {}; - template <> struct FromTypeToType<float,double> : public boost::true_type {}; - template <> struct FromTypeToType<float,std::complex<double> > : public boost::true_type {}; - template <> struct FromTypeToType<float,long double> : public boost::true_type {}; - template <> struct FromTypeToType<float,std::complex<long double> > : public boost::true_type {}; - - template <> struct FromTypeToType<double,std::complex<double> > : public boost::true_type {}; - template <> struct FromTypeToType<double,long double> : public boost::true_type {}; - template <> struct FromTypeToType<double,std::complex<long double> > : public boost::true_type {}; - - namespace bp = boost::python; - - template<typename MatType, bool IsVectorAtCompileTime = MatType::IsVectorAtCompileTime> - struct initEigenObject - { - static MatType * run(PyArrayObject * pyArray, void * storage) - { - assert(PyArray_NDIM(pyArray) == 1 || PyArray_NDIM(pyArray) == 2); - - int rows = -1, cols = -1; - if(PyArray_NDIM(pyArray) == 2) - { - rows = (int)PyArray_DIMS(pyArray)[0]; - cols = (int)PyArray_DIMS(pyArray)[1]; - } - else if(PyArray_NDIM(pyArray) == 1) - { - rows = (int)PyArray_DIMS(pyArray)[0]; - cols = 1; - } - - return new (storage) MatType(rows,cols); - } - }; - - template<typename MatType> - struct initEigenObject<MatType,true> - { - static MatType * run(PyArrayObject * pyArray, void * storage) - { - if(PyArray_NDIM(pyArray) == 1) - { - const int rows_or_cols = (int)PyArray_DIMS(pyArray)[0]; - return new (storage) MatType(rows_or_cols); - } - else - { - const int rows = (int)PyArray_DIMS(pyArray)[0]; - const int cols = (int)PyArray_DIMS(pyArray)[1]; - return new (storage) MatType(rows,cols); - } - } - }; - - template<typename Scalar, typename NewScalar, bool cast_is_valid = FromTypeToType<Scalar,NewScalar>::value > - struct CastMatToMat - { - template<typename MatrixIn, typename MatrixOut> - static void run(const Eigen::MatrixBase<MatrixIn> & input, - const Eigen::MatrixBase<MatrixOut> & dest) - { - MatrixOut & dest_ = const_cast<MatrixOut &>(dest.derived()); - if(dest.rows() == input.rows()) - dest_ = input.template cast<NewScalar>(); - else - dest_ = input.transpose().template cast<NewScalar>(); - } - }; - - template<typename Scalar, typename NewScalar> - struct CastMatToMat<Scalar,NewScalar,false> - { - template<typename MatrixIn, typename MatrixOut> - static void run(const Eigen::MatrixBase<MatrixIn> & /*input*/, - const Eigen::MatrixBase<MatrixOut> & /*dest*/) - { - // do nothing - assert("Must never happened"); - } - }; - -#define EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType,Scalar,NewScalar,pyArray,mat) \ - CastMatToMat<Scalar,NewScalar>::run(MapNumpy<MatType,Scalar>::map(pyArray),mat) - -#define EIGENPY_CAST_FROM_EIGEN_MATRIX_TO_PYARRAY(MatType,Scalar,NewScalar,mat,pyArray) \ - CastMatToMat<Scalar,NewScalar>::run(mat,MapNumpy<MatType,NewScalar>::map(pyArray)) - - template<typename MatType> - struct EigenObjectAllocator - { - typedef MatType Type; - typedef typename MatType::Scalar Scalar; - - static void allocate(PyArrayObject * pyArray, void * storage) - { - Type * mat_ptr = initEigenObject<Type>::run(pyArray,storage); - Type & mat = *mat_ptr; - - const int pyArray_Type = GET_PY_ARRAY_TYPE(pyArray); - if(pyArray_Type == NumpyEquivalentType<Scalar>::type_code) - { - mat = MapNumpy<MatType,Scalar>::map(pyArray); // avoid useless cast - return; - } - - switch(pyArray_Type) - { - case NPY_INT: - EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType,int,Scalar,pyArray,mat); - break; - case NPY_LONG: - EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType,long,Scalar,pyArray,mat); - break; - case NPY_FLOAT: - EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType,float,Scalar,pyArray,mat); - break; - case NPY_CFLOAT: - EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType,std::complex<float>,Scalar,pyArray,mat); - break; - case NPY_DOUBLE: - EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType,double,Scalar,pyArray,mat); - break; - case NPY_CDOUBLE: - EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType,std::complex<double>,Scalar,pyArray,mat); - break; - case NPY_LONGDOUBLE: - EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType,long double,Scalar,pyArray,mat); - break; - case NPY_CLONGDOUBLE: - EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType,std::complex<long double>,Scalar,pyArray,mat); - break; - default: - throw Exception("You asked for a conversion which is not implemented."); - } - } - - /// \brief Copy mat into the Python array using Eigen::Map - template<typename MatrixDerived> - static void copy(const Eigen::MatrixBase<MatrixDerived> & mat_, - PyArrayObject * pyArray) - { - const MatrixDerived & mat = const_cast<const MatrixDerived &>(mat_.derived()); - const int pyArray_Type = GET_PY_ARRAY_TYPE(pyArray); - - typedef typename MapNumpy<MatType,Scalar>::EigenMap MapType; - - if(pyArray_Type == NumpyEquivalentType<Scalar>::type_code) // no cast needed - { - MapType map_pyArray = MapNumpy<MatType,Scalar>::map(pyArray); - if(mat.rows() == map_pyArray.rows()) - map_pyArray = mat; - else - map_pyArray = mat.transpose(); - return; - } - - switch(pyArray_Type) - { - case NPY_INT: - EIGENPY_CAST_FROM_EIGEN_MATRIX_TO_PYARRAY(MatType,Scalar,int,mat,pyArray); - break; - case NPY_LONG: - EIGENPY_CAST_FROM_EIGEN_MATRIX_TO_PYARRAY(MatType,Scalar,long,mat,pyArray); - break; - case NPY_FLOAT: - EIGENPY_CAST_FROM_EIGEN_MATRIX_TO_PYARRAY(MatType,Scalar,float,mat,pyArray); - break; - case NPY_CFLOAT: - EIGENPY_CAST_FROM_EIGEN_MATRIX_TO_PYARRAY(MatType,Scalar,std::complex<float>,mat,pyArray); - break; - case NPY_DOUBLE: - EIGENPY_CAST_FROM_EIGEN_MATRIX_TO_PYARRAY(MatType,Scalar,double,mat,pyArray); - break; - case NPY_CDOUBLE: - EIGENPY_CAST_FROM_EIGEN_MATRIX_TO_PYARRAY(MatType,Scalar,std::complex<double>,mat,pyArray); - break; - case NPY_LONGDOUBLE: - EIGENPY_CAST_FROM_EIGEN_MATRIX_TO_PYARRAY(MatType,Scalar,long double,mat,pyArray); - break; - case NPY_CLONGDOUBLE: - EIGENPY_CAST_FROM_EIGEN_MATRIX_TO_PYARRAY(MatType,Scalar,std::complex<long double>,mat,pyArray); - break; - default: - throw Exception("You asked for a conversion which is not implemented."); - } - } - }; - -#if EIGEN_VERSION_AT_LEAST(3,2,0) - template<typename MatType> - struct EigenObjectAllocator< eigenpy::Ref<MatType> > - { - typedef eigenpy::Ref<MatType> Type; - typedef typename MatType::Scalar Scalar; - - static void allocate(PyArrayObject * pyArray, void * storage) - { - typename MapNumpy<MatType,Scalar>::EigenMap numpyMap = MapNumpy<MatType,Scalar>::map(pyArray); - new (storage) Type(numpyMap); - } - - static void copy(Type const & mat, PyArrayObject * pyArray) - { - EigenObjectAllocator<MatType>::copy(mat,pyArray); - } - }; -#endif - - /* --- TO PYTHON -------------------------------------------------------------- */ - - template<typename MatType> - struct EigenToPy - { - static PyObject* convert(MatType const & mat) - { - typedef typename MatType::Scalar Scalar; - assert( (mat.rows()<INT_MAX) && (mat.cols()<INT_MAX) - && "Matrix range larger than int ... should never happen." ); - const npy_intp R = (npy_intp)mat.rows(), C = (npy_intp)mat.cols(); - - PyArrayObject* pyArray; - // Allocate Python memory - if( ( ((!(C == 1) != !(R == 1)) && !MatType::IsVectorAtCompileTime) || MatType::IsVectorAtCompileTime) - && NumpyType::getType() == ARRAY_TYPE) // Handle array with a single dimension - { - npy_intp shape[1] = { C == 1 ? R : C }; - pyArray = (PyArrayObject*) PyArray_SimpleNew(1, shape, - NumpyEquivalentType<Scalar>::type_code); - } - else - { - npy_intp shape[2] = { R,C }; - pyArray = (PyArrayObject*) PyArray_SimpleNew(2, shape, - NumpyEquivalentType<Scalar>::type_code); - } - - // Allocate memory - EigenObjectAllocator<MatType>::copy(mat,pyArray); - - // Create an instance (either np.array or np.matrix) - return NumpyType::getInstance().make(pyArray).ptr(); - } - }; - - /* --- FROM PYTHON ------------------------------------------------------------ */ - - template<typename MatType> - struct EigenFromPy - { - - static bool isScalarConvertible(const int np_type) - { - if(NumpyEquivalentType<typename MatType::Scalar>::type_code == np_type) - return true; - - switch(np_type) - { - case NPY_INT: - return FromTypeToType<int,typename MatType::Scalar>::value; - case NPY_LONG: - return FromTypeToType<long,typename MatType::Scalar>::value; - case NPY_FLOAT: - return FromTypeToType<float,typename MatType::Scalar>::value; - case NPY_CFLOAT: - return FromTypeToType<std::complex<float>,typename MatType::Scalar>::value; - case NPY_DOUBLE: - return FromTypeToType<double,typename MatType::Scalar>::value; - case NPY_CDOUBLE: - return FromTypeToType<std::complex<double>,typename MatType::Scalar>::value; - case NPY_LONGDOUBLE: - return FromTypeToType<long double,typename MatType::Scalar>::value; - case NPY_CLONGDOUBLE: - return FromTypeToType<std::complex<long double>,typename MatType::Scalar>::value; - default: - return false; - } - } - - /// \brief Determine if pyObj can be converted into a MatType object - static void* convertible(PyArrayObject* pyArray) - { - if(!PyArray_Check(pyArray)) - return 0; - - if(!isScalarConvertible(GET_PY_ARRAY_TYPE(pyArray))) - return 0; - - if(MatType::IsVectorAtCompileTime) - { - const Eigen::DenseIndex size_at_compile_time - = MatType::IsRowMajor - ? MatType::ColsAtCompileTime - : MatType::RowsAtCompileTime; - - switch(PyArray_NDIM(pyArray)) - { - case 0: - return 0; - case 1: - { - if(size_at_compile_time != Eigen::Dynamic) - { - // check that the sizes at compile time matche - if(PyArray_DIMS(pyArray)[0] == size_at_compile_time) - return pyArray; - else - return 0; - } - else // This is a dynamic MatType - return pyArray; - } - case 2: - { - // Special care of scalar matrix of dimension 1x1. - if(PyArray_DIMS(pyArray)[0] == 1 && PyArray_DIMS(pyArray)[1] == 1) - { - if(size_at_compile_time != Eigen::Dynamic) - { - if(size_at_compile_time == 1) - return pyArray; - else - return 0; - } - else // This is a dynamic MatType - return pyArray; - } - - if(PyArray_DIMS(pyArray)[0] > 1 && PyArray_DIMS(pyArray)[1] > 1) - { -#ifndef NDEBUG - std::cerr << "The number of dimension of the object does not correspond to a vector" << std::endl; -#endif - return 0; - } - - if(((PyArray_DIMS(pyArray)[0] == 1) && (MatType::ColsAtCompileTime == 1)) - || ((PyArray_DIMS(pyArray)[1] == 1) && (MatType::RowsAtCompileTime == 1))) - { -#ifndef NDEBUG - if(MatType::ColsAtCompileTime == 1) - std::cerr << "The object is not a column vector" << std::endl; - else - std::cerr << "The object is not a row vector" << std::endl; -#endif - return 0; - } - - if(size_at_compile_time != Eigen::Dynamic) - { // This is a fixe size vector - const Eigen::DenseIndex pyArray_size - = PyArray_DIMS(pyArray)[0] > PyArray_DIMS(pyArray)[1] - ? PyArray_DIMS(pyArray)[0] - : PyArray_DIMS(pyArray)[1]; - if(size_at_compile_time != pyArray_size) - return 0; - } - break; - } - default: - return 0; - } - } - else // this is a matrix - { - if(PyArray_NDIM(pyArray) == 1) // We can always convert a vector into a matrix - { - return pyArray; - } - - if(PyArray_NDIM(pyArray) != 2) - { -#ifndef NDEBUG - std::cerr << "The number of dimension of the object is not correct." << std::endl; -#endif - return 0; - } - - if(PyArray_NDIM(pyArray) == 2) - { - const int R = (int)PyArray_DIMS(pyArray)[0]; - const int C = (int)PyArray_DIMS(pyArray)[1]; - - if( (MatType::RowsAtCompileTime!=R) - && (MatType::RowsAtCompileTime!=Eigen::Dynamic) ) - return 0; - if( (MatType::ColsAtCompileTime!=C) - && (MatType::ColsAtCompileTime!=Eigen::Dynamic) ) - return 0; - } - } - -#ifdef NPY_1_8_API_VERSION - if(!(PyArray_FLAGS(pyArray))) -#else - if(!(PyArray_FLAGS(pyArray) & NPY_ALIGNED)) -#endif - { -#ifndef NDEBUG - std::cerr << "NPY non-aligned matrices are not implemented." << std::endl; -#endif - return 0; - } - - return pyArray; - } - - /// \brief Allocate memory and copy pyObj in the new storage - static void construct(PyObject* pyObj, - bp::converter::rvalue_from_python_stage1_data* memory) - { - PyArrayObject * pyArray = reinterpret_cast<PyArrayObject*>(pyObj); - assert((PyArray_DIMS(pyArray)[0]<INT_MAX) && (PyArray_DIMS(pyArray)[1]<INT_MAX)); - - void* storage = reinterpret_cast<bp::converter::rvalue_from_python_storage<MatType>*> - (reinterpret_cast<void*>(memory))->storage.bytes; - - EigenObjectAllocator<MatType>::allocate(pyArray,storage); - - memory->convertible = storage; - } - - static void registration() - { - bp::converter::registry::push_back - (reinterpret_cast<void *(*)(_object *)>(&EigenFromPy::convertible), - &EigenFromPy::construct,bp::type_id<MatType>()); - } - }; - template<typename MatType,typename EigenEquivalentType> EIGENPY_DEPRECATED void enableEigenPySpecific() { enableEigenPySpecific<MatType>(); } - - template<typename MatType> - struct EigenFromPyConverter - { - static void registration() - { - EigenFromPy<MatType>::registration(); - - // Add also conversion to Eigen::MatrixBase<MatType> - typedef Eigen::MatrixBase<MatType> MatrixBase; - EigenFromPy<MatrixBase>::registration(); - - // Add also conversion to Eigen::EigenBase<MatType> - typedef Eigen::EigenBase<MatType> EigenBase; - EigenFromPy<EigenBase>::registration(); - } - }; - - template<typename MatType> - struct EigenFromPy< Eigen::MatrixBase<MatType> > : EigenFromPy<MatType> - { - typedef EigenFromPy<MatType> EigenFromPyDerived; - typedef Eigen::MatrixBase<MatType> Base; - - static void registration() - { - bp::converter::registry::push_back - (reinterpret_cast<void *(*)(_object *)>(&EigenFromPy::convertible), - &EigenFromPy::construct,bp::type_id<Base>()); - } - }; - - template<typename MatType> - struct EigenFromPy< Eigen::EigenBase<MatType> > : EigenFromPy<MatType> - { - typedef EigenFromPy<MatType> EigenFromPyDerived; - typedef Eigen::EigenBase<MatType> Base; - - static void registration() - { - bp::converter::registry::push_back - (reinterpret_cast<void *(*)(_object *)>(&EigenFromPy::convertible), - &EigenFromPy::construct,bp::type_id<Base>()); - } - }; - -#if EIGEN_VERSION_AT_LEAST(3,2,0) - // Template specialization for Eigen::Ref - template<typename MatType> - struct EigenFromPyConverter< eigenpy::Ref<MatType> > - { - static void registration() - { - bp::converter::registry::push_back - (reinterpret_cast<void *(*)(_object *)>(&EigenFromPy<MatType>::convertible), - &EigenFromPy<MatType>::construct,bp::type_id<MatType>()); - } - }; -#endif -#define numpy_import_array() {if (_import_array() < 0) {PyErr_Print(); PyErr_SetString(PyExc_ImportError, "numpy.core.multiarray failed to import"); } } - template<typename MatType> void enableEigenPySpecific() { - numpy_import_array(); if(check_registration<MatType>()) return; - bp::to_python_converter<MatType,EigenToPy<MatType> >(); + EigenToPyConverter<MatType>::registration(); EigenFromPyConverter<MatType>::registration(); } diff --git a/include/eigenpy/details/rvalue_from_python_data.hpp b/include/eigenpy/details/rvalue_from_python_data.hpp deleted file mode 100644 index 254961b9fb3a2a69848e9364ba8e3c16f195c735..0000000000000000000000000000000000000000 --- a/include/eigenpy/details/rvalue_from_python_data.hpp +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2018-2020, INRIA - */ - -#ifndef __eigenpy_details_rvalue_from_python_data_hpp__ -#define __eigenpy_details_rvalue_from_python_data_hpp__ - -#include <boost/python/converter/rvalue_from_python_data.hpp> -#include <Eigen/Core> - -namespace boost -{ - namespace python - { - namespace converter - { - - /// \brief Template specialization of rvalue_from_python_data - template<typename Derived> - struct rvalue_from_python_data<Eigen::MatrixBase<Derived> const & > - : rvalue_from_python_storage<Derived const & > - { - typedef Eigen::MatrixBase<Derived> const & T; - -# if (!defined(__MWERKS__) || __MWERKS__ >= 0x3000) \ -&& (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 245) \ -&& (!defined(__DECCXX_VER) || __DECCXX_VER > 60590014) \ -&& !defined(BOOST_PYTHON_SYNOPSIS) /* Synopsis' OpenCXX has trouble parsing this */ - // This must always be a POD struct with m_data its first member. - BOOST_STATIC_ASSERT(BOOST_PYTHON_OFFSETOF(rvalue_from_python_storage<T>,stage1) == 0); -# endif - - // The usual constructor - rvalue_from_python_data(rvalue_from_python_stage1_data const & _stage1) - { - this->stage1 = _stage1; - } - - // This constructor just sets m_convertible -- used by - // implicitly_convertible<> to perform the final step of the - // conversion, where the construct() function is already known. - rvalue_from_python_data(void* convertible) - { - this->stage1.convertible = convertible; - } - - // Destroys any object constructed in the storage. - ~rvalue_from_python_data() - { - if (this->stage1.convertible == this->storage.bytes) - static_cast<Derived *>((void *)this->storage.bytes)->~Derived(); - } - }; - - /// \brief Template specialization of rvalue_from_python_data - template<typename Derived> - struct rvalue_from_python_data<Eigen::EigenBase<Derived> const & > - : rvalue_from_python_storage<Derived const & > - { - typedef Eigen::MatrixBase<Derived> const & T; - -# if (!defined(__MWERKS__) || __MWERKS__ >= 0x3000) \ -&& (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 245) \ -&& (!defined(__DECCXX_VER) || __DECCXX_VER > 60590014) \ -&& !defined(BOOST_PYTHON_SYNOPSIS) /* Synopsis' OpenCXX has trouble parsing this */ - // This must always be a POD struct with m_data its first member. - BOOST_STATIC_ASSERT(BOOST_PYTHON_OFFSETOF(rvalue_from_python_storage<T>,stage1) == 0); -# endif - - // The usual constructor - rvalue_from_python_data(rvalue_from_python_stage1_data const & _stage1) - { - this->stage1 = _stage1; - } - - // This constructor just sets m_convertible -- used by - // implicitly_convertible<> to perform the final step of the - // conversion, where the construct() function is already known. - rvalue_from_python_data(void* convertible) - { - this->stage1.convertible = convertible; - } - - // Destroys any object constructed in the storage. - ~rvalue_from_python_data() - { - if (this->stage1.convertible == this->storage.bytes) - static_cast<Derived *>((void *)this->storage.bytes)->~Derived(); - } - }; - - } - } -} // namespace boost::python::converter - -#endif // ifndef __eigenpy_details_rvalue_from_python_data_hpp__ diff --git a/include/eigenpy/eigen-allocator.hpp b/include/eigenpy/eigen-allocator.hpp new file mode 100644 index 0000000000000000000000000000000000000000..243c1d3d4d1ba870c965af770cd11434cbf5b499 --- /dev/null +++ b/include/eigenpy/eigen-allocator.hpp @@ -0,0 +1,216 @@ +// +// Copyright (c) 2014-2020 CNRS INRIA +// + +#ifndef __eigenpy_eigen_allocator_hpp__ +#define __eigenpy_eigen_allocator_hpp__ + +#include "eigenpy/fwd.hpp" +#include "eigenpy/map.hpp" +#include "eigenpy/scalar-conversion.hpp" + +namespace eigenpy +{ + + namespace details + { + template<typename MatType, bool IsVectorAtCompileTime = MatType::IsVectorAtCompileTime> + struct init_matrix_or_array + { + static MatType * run(PyArrayObject * pyArray, void * storage) + { + assert(PyArray_NDIM(pyArray) == 1 || PyArray_NDIM(pyArray) == 2); + + int rows = -1, cols = -1; + if(PyArray_NDIM(pyArray) == 2) + { + rows = (int)PyArray_DIMS(pyArray)[0]; + cols = (int)PyArray_DIMS(pyArray)[1]; + } + else if(PyArray_NDIM(pyArray) == 1) + { + rows = (int)PyArray_DIMS(pyArray)[0]; + cols = 1; + } + + return new (storage) MatType(rows,cols); + } + }; + + template<typename MatType> + struct init_matrix_or_array<MatType,true> + { + static MatType * run(PyArrayObject * pyArray, void * storage) + { + if(PyArray_NDIM(pyArray) == 1) + { + const int rows_or_cols = (int)PyArray_DIMS(pyArray)[0]; + return new (storage) MatType(rows_or_cols); + } + else + { + const int rows = (int)PyArray_DIMS(pyArray)[0]; + const int cols = (int)PyArray_DIMS(pyArray)[1]; + return new (storage) MatType(rows,cols); + } + } + }; + + template<typename Scalar, typename NewScalar, bool cast_is_valid = FromTypeToType<Scalar,NewScalar>::value > + struct cast_matrix_or_array + { + template<typename MatrixIn, typename MatrixOut> + static void run(const Eigen::MatrixBase<MatrixIn> & input, + const Eigen::MatrixBase<MatrixOut> & dest) + { + MatrixOut & dest_ = const_cast<MatrixOut &>(dest.derived()); + if(dest.rows() == input.rows()) + dest_ = input.template cast<NewScalar>(); + else + dest_ = input.transpose().template cast<NewScalar>(); + } + }; + + template<typename Scalar, typename NewScalar> + struct cast_matrix_or_array<Scalar,NewScalar,false> + { + template<typename MatrixIn, typename MatrixOut> + static void run(const Eigen::MatrixBase<MatrixIn> & /*input*/, + const Eigen::MatrixBase<MatrixOut> & /*dest*/) + { + // do nothing + assert("Must never happened"); + } + }; + + } // namespace details + +#define EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType,Scalar,NewScalar,pyArray,mat) \ + details::cast_matrix_or_array<Scalar,NewScalar>::run(MapNumpy<MatType,Scalar>::map(pyArray),mat) + +#define EIGENPY_CAST_FROM_EIGEN_MATRIX_TO_PYARRAY(MatType,Scalar,NewScalar,mat,pyArray) \ + details::cast_matrix_or_array<Scalar,NewScalar>::run(mat,MapNumpy<MatType,NewScalar>::map(pyArray)) + + template<typename MatType> + struct EigenAllocator + { + typedef MatType Type; + typedef typename MatType::Scalar Scalar; + + static void allocate(PyArrayObject * pyArray, void * storage) + { + Type * mat_ptr = details::init_matrix_or_array<Type>::run(pyArray,storage); + Type & mat = *mat_ptr; + + const int pyArray_Type = EIGENPY_GET_PY_ARRAY_TYPE(pyArray); + if(pyArray_Type == NumpyEquivalentType<Scalar>::type_code) + { + mat = MapNumpy<MatType,Scalar>::map(pyArray); // avoid useless cast + return; + } + + switch(pyArray_Type) + { + case NPY_INT: + EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType,int,Scalar,pyArray,mat); + break; + case NPY_LONG: + EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType,long,Scalar,pyArray,mat); + break; + case NPY_FLOAT: + EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType,float,Scalar,pyArray,mat); + break; + case NPY_CFLOAT: + EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType,std::complex<float>,Scalar,pyArray,mat); + break; + case NPY_DOUBLE: + EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType,double,Scalar,pyArray,mat); + break; + case NPY_CDOUBLE: + EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType,std::complex<double>,Scalar,pyArray,mat); + break; + case NPY_LONGDOUBLE: + EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType,long double,Scalar,pyArray,mat); + break; + case NPY_CLONGDOUBLE: + EIGENPY_CAST_FROM_PYARRAY_TO_EIGEN_MATRIX(MatType,std::complex<long double>,Scalar,pyArray,mat); + break; + default: + throw Exception("You asked for a conversion which is not implemented."); + } + } + + /// \brief Copy mat into the Python array using Eigen::Map + template<typename MatrixDerived> + static void copy(const Eigen::MatrixBase<MatrixDerived> & mat_, + PyArrayObject * pyArray) + { + const MatrixDerived & mat = const_cast<const MatrixDerived &>(mat_.derived()); + const int pyArray_Type = EIGENPY_GET_PY_ARRAY_TYPE(pyArray); + + typedef typename MapNumpy<MatType,Scalar>::EigenMap MapType; + + if(pyArray_Type == NumpyEquivalentType<Scalar>::type_code) // no cast needed + { + MapType map_pyArray = MapNumpy<MatType,Scalar>::map(pyArray); + if(mat.rows() == map_pyArray.rows()) + map_pyArray = mat; + else + map_pyArray = mat.transpose(); + return; + } + + switch(pyArray_Type) + { + case NPY_INT: + EIGENPY_CAST_FROM_EIGEN_MATRIX_TO_PYARRAY(MatType,Scalar,int,mat,pyArray); + break; + case NPY_LONG: + EIGENPY_CAST_FROM_EIGEN_MATRIX_TO_PYARRAY(MatType,Scalar,long,mat,pyArray); + break; + case NPY_FLOAT: + EIGENPY_CAST_FROM_EIGEN_MATRIX_TO_PYARRAY(MatType,Scalar,float,mat,pyArray); + break; + case NPY_CFLOAT: + EIGENPY_CAST_FROM_EIGEN_MATRIX_TO_PYARRAY(MatType,Scalar,std::complex<float>,mat,pyArray); + break; + case NPY_DOUBLE: + EIGENPY_CAST_FROM_EIGEN_MATRIX_TO_PYARRAY(MatType,Scalar,double,mat,pyArray); + break; + case NPY_CDOUBLE: + EIGENPY_CAST_FROM_EIGEN_MATRIX_TO_PYARRAY(MatType,Scalar,std::complex<double>,mat,pyArray); + break; + case NPY_LONGDOUBLE: + EIGENPY_CAST_FROM_EIGEN_MATRIX_TO_PYARRAY(MatType,Scalar,long double,mat,pyArray); + break; + case NPY_CLONGDOUBLE: + EIGENPY_CAST_FROM_EIGEN_MATRIX_TO_PYARRAY(MatType,Scalar,std::complex<long double>,mat,pyArray); + break; + default: + throw Exception("You asked for a conversion which is not implemented."); + } + } + }; + +#if EIGEN_VERSION_AT_LEAST(3,2,0) + template<typename MatType> + struct EigenAllocator< eigenpy::Ref<MatType> > + { + typedef eigenpy::Ref<MatType> Type; + typedef typename MatType::Scalar Scalar; + + static void allocate(PyArrayObject * pyArray, void * storage) + { + typename MapNumpy<MatType,Scalar>::EigenMap numpyMap = MapNumpy<MatType,Scalar>::map(pyArray); + new (storage) Type(numpyMap); + } + + static void copy(Type const & mat, PyArrayObject * pyArray) + { + EigenAllocator<MatType>::copy(mat,pyArray); + } + }; +#endif +} + +#endif // __eigenpy_eigen_allocator_hpp__ diff --git a/include/eigenpy/eigen-from-python.hpp b/include/eigenpy/eigen-from-python.hpp new file mode 100644 index 0000000000000000000000000000000000000000..b5717e330dde421ae9785eaee674aeff7a807413 --- /dev/null +++ b/include/eigenpy/eigen-from-python.hpp @@ -0,0 +1,324 @@ +// +// Copyright (c) 2014-2020 CNRS INRIA +// + +#ifndef __eigenpy_eigen_from_python_hpp__ +#define __eigenpy_eigen_from_python_hpp__ + +#include "eigenpy/fwd.hpp" +#include "eigenpy/numpy-type.hpp" +#include "eigenpy/eigen-allocator.hpp" +#include "eigenpy/scalar-conversion.hpp" + +#include <boost/python/converter/rvalue_from_python_data.hpp> + +namespace boost { namespace python { namespace converter { + + /// \brief Template specialization of rvalue_from_python_data + template<typename Derived> + struct rvalue_from_python_data<Eigen::MatrixBase<Derived> const & > + : rvalue_from_python_storage<Derived const & > + { + typedef Eigen::MatrixBase<Derived> const & T; + +# if (!defined(__MWERKS__) || __MWERKS__ >= 0x3000) \ +&& (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 245) \ +&& (!defined(__DECCXX_VER) || __DECCXX_VER > 60590014) \ +&& !defined(BOOST_PYTHON_SYNOPSIS) /* Synopsis' OpenCXX has trouble parsing this */ + // This must always be a POD struct with m_data its first member. + BOOST_STATIC_ASSERT(BOOST_PYTHON_OFFSETOF(rvalue_from_python_storage<T>,stage1) == 0); +# endif + + // The usual constructor + rvalue_from_python_data(rvalue_from_python_stage1_data const & _stage1) + { + this->stage1 = _stage1; + } + + // This constructor just sets m_convertible -- used by + // implicitly_convertible<> to perform the final step of the + // conversion, where the construct() function is already known. + rvalue_from_python_data(void* convertible) + { + this->stage1.convertible = convertible; + } + + // Destroys any object constructed in the storage. + ~rvalue_from_python_data() + { + if (this->stage1.convertible == this->storage.bytes) + static_cast<Derived *>((void *)this->storage.bytes)->~Derived(); + } + }; + + /// \brief Template specialization of rvalue_from_python_data + template<typename Derived> + struct rvalue_from_python_data<Eigen::EigenBase<Derived> const & > + : rvalue_from_python_storage<Derived const & > + { + typedef Eigen::EigenBase<Derived> const & T; + +# if (!defined(__MWERKS__) || __MWERKS__ >= 0x3000) \ +&& (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 245) \ +&& (!defined(__DECCXX_VER) || __DECCXX_VER > 60590014) \ +&& !defined(BOOST_PYTHON_SYNOPSIS) /* Synopsis' OpenCXX has trouble parsing this */ + // This must always be a POD struct with m_data its first member. + BOOST_STATIC_ASSERT(BOOST_PYTHON_OFFSETOF(rvalue_from_python_storage<T>,stage1) == 0); +# endif + + // The usual constructor + rvalue_from_python_data(rvalue_from_python_stage1_data const & _stage1) + { + this->stage1 = _stage1; + } + + // This constructor just sets m_convertible -- used by + // implicitly_convertible<> to perform the final step of the + // conversion, where the construct() function is already known. + rvalue_from_python_data(void* convertible) + { + this->stage1.convertible = convertible; + } + + // Destroys any object constructed in the storage. + ~rvalue_from_python_data() + { + if (this->stage1.convertible == this->storage.bytes) + static_cast<Derived *>((void *)this->storage.bytes)->~Derived(); + } + }; + +} } } + +namespace eigenpy +{ + template<typename MatType> + struct EigenFromPy + { + + static bool isScalarConvertible(const int np_type) + { + if(NumpyEquivalentType<typename MatType::Scalar>::type_code == np_type) + return true; + + switch(np_type) + { + case NPY_INT: + return FromTypeToType<int,typename MatType::Scalar>::value; + case NPY_LONG: + return FromTypeToType<long,typename MatType::Scalar>::value; + case NPY_FLOAT: + return FromTypeToType<float,typename MatType::Scalar>::value; + case NPY_CFLOAT: + return FromTypeToType<std::complex<float>,typename MatType::Scalar>::value; + case NPY_DOUBLE: + return FromTypeToType<double,typename MatType::Scalar>::value; + case NPY_CDOUBLE: + return FromTypeToType<std::complex<double>,typename MatType::Scalar>::value; + case NPY_LONGDOUBLE: + return FromTypeToType<long double,typename MatType::Scalar>::value; + case NPY_CLONGDOUBLE: + return FromTypeToType<std::complex<long double>,typename MatType::Scalar>::value; + default: + return false; + } + } + + /// \brief Determine if pyObj can be converted into a MatType object + static void* convertible(PyArrayObject* pyArray) + { + if(!PyArray_Check(pyArray)) + return 0; + + if(!isScalarConvertible(EIGENPY_GET_PY_ARRAY_TYPE(pyArray))) + return 0; + + if(MatType::IsVectorAtCompileTime) + { + const Eigen::DenseIndex size_at_compile_time + = MatType::IsRowMajor + ? MatType::ColsAtCompileTime + : MatType::RowsAtCompileTime; + + switch(PyArray_NDIM(pyArray)) + { + case 0: + return 0; + case 1: + { + if(size_at_compile_time != Eigen::Dynamic) + { + // check that the sizes at compile time matche + if(PyArray_DIMS(pyArray)[0] == size_at_compile_time) + return pyArray; + else + return 0; + } + else // This is a dynamic MatType + return pyArray; + } + case 2: + { + // Special care of scalar matrix of dimension 1x1. + if(PyArray_DIMS(pyArray)[0] == 1 && PyArray_DIMS(pyArray)[1] == 1) + { + if(size_at_compile_time != Eigen::Dynamic) + { + if(size_at_compile_time == 1) + return pyArray; + else + return 0; + } + else // This is a dynamic MatType + return pyArray; + } + + if(PyArray_DIMS(pyArray)[0] > 1 && PyArray_DIMS(pyArray)[1] > 1) + { + return 0; + } + + if(((PyArray_DIMS(pyArray)[0] == 1) && (MatType::ColsAtCompileTime == 1)) + || ((PyArray_DIMS(pyArray)[1] == 1) && (MatType::RowsAtCompileTime == 1))) + { + return 0; + } + + if(size_at_compile_time != Eigen::Dynamic) + { // This is a fixe size vector + const Eigen::DenseIndex pyArray_size + = PyArray_DIMS(pyArray)[0] > PyArray_DIMS(pyArray)[1] + ? PyArray_DIMS(pyArray)[0] + : PyArray_DIMS(pyArray)[1]; + if(size_at_compile_time != pyArray_size) + return 0; + } + break; + } + default: + return 0; + } + } + else // this is a matrix + { + if(PyArray_NDIM(pyArray) == 1) // We can always convert a vector into a matrix + { + return pyArray; + } + + if(PyArray_NDIM(pyArray) != 2) + { + return 0; + } + + if(PyArray_NDIM(pyArray) == 2) + { + const int R = (int)PyArray_DIMS(pyArray)[0]; + const int C = (int)PyArray_DIMS(pyArray)[1]; + + if( (MatType::RowsAtCompileTime!=R) + && (MatType::RowsAtCompileTime!=Eigen::Dynamic) ) + return 0; + if( (MatType::ColsAtCompileTime!=C) + && (MatType::ColsAtCompileTime!=Eigen::Dynamic) ) + return 0; + } + } + +#ifdef NPY_1_8_API_VERSION + if(!(PyArray_FLAGS(pyArray))) +#else + if(!(PyArray_FLAGS(pyArray) & NPY_ALIGNED)) +#endif + { + return 0; + } + + return pyArray; + } + + /// \brief Allocate memory and copy pyObj in the new storage + static void construct(PyObject* pyObj, + bp::converter::rvalue_from_python_stage1_data* memory) + { + PyArrayObject * pyArray = reinterpret_cast<PyArrayObject*>(pyObj); + assert((PyArray_DIMS(pyArray)[0]<INT_MAX) && (PyArray_DIMS(pyArray)[1]<INT_MAX)); + + void* storage = reinterpret_cast<bp::converter::rvalue_from_python_storage<MatType>*> + (reinterpret_cast<void*>(memory))->storage.bytes; + + EigenAllocator<MatType>::allocate(pyArray,storage); + + memory->convertible = storage; + } + + static void registration() + { + bp::converter::registry::push_back + (reinterpret_cast<void *(*)(_object *)>(&EigenFromPy::convertible), + &EigenFromPy::construct,bp::type_id<MatType>()); + } + }; + + template<typename MatType> + struct EigenFromPyConverter + { + static void registration() + { + EigenFromPy<MatType>::registration(); + + // Add also conversion to Eigen::MatrixBase<MatType> + typedef Eigen::MatrixBase<MatType> MatrixBase; + EigenFromPy<MatrixBase>::registration(); + + // Add also conversion to Eigen::EigenBase<MatType> + typedef Eigen::EigenBase<MatType> EigenBase; + EigenFromPy<EigenBase>::registration(); + } + }; + + template<typename MatType> + struct EigenFromPy< Eigen::MatrixBase<MatType> > : EigenFromPy<MatType> + { + typedef EigenFromPy<MatType> EigenFromPyDerived; + typedef Eigen::MatrixBase<MatType> Base; + + static void registration() + { + bp::converter::registry::push_back + (reinterpret_cast<void *(*)(_object *)>(&EigenFromPy::convertible), + &EigenFromPy::construct,bp::type_id<Base>()); + } + }; + + template<typename MatType> + struct EigenFromPy< Eigen::EigenBase<MatType> > : EigenFromPy<MatType> + { + typedef EigenFromPy<MatType> EigenFromPyDerived; + typedef Eigen::EigenBase<MatType> Base; + + static void registration() + { + bp::converter::registry::push_back + (reinterpret_cast<void *(*)(_object *)>(&EigenFromPy::convertible), + &EigenFromPy::construct,bp::type_id<Base>()); + } + }; + +#if EIGEN_VERSION_AT_LEAST(3,2,0) + // Template specialization for Eigen::Ref + template<typename MatType> + struct EigenFromPyConverter< eigenpy::Ref<MatType> > + { + static void registration() + { + bp::converter::registry::push_back + (reinterpret_cast<void *(*)(_object *)>(&EigenFromPy<MatType>::convertible), + &EigenFromPy<MatType>::construct,bp::type_id<MatType>()); + } + }; + +#endif +} + +#endif // __eigenpy_eigen_from_python_hpp__ diff --git a/include/eigenpy/eigen-to-python.hpp b/include/eigenpy/eigen-to-python.hpp new file mode 100644 index 0000000000000000000000000000000000000000..cc990519f62ea103b22323d0ea77b01ad2310b19 --- /dev/null +++ b/include/eigenpy/eigen-to-python.hpp @@ -0,0 +1,105 @@ +// +// Copyright (c) 2014-2020 CNRS INRIA +// + +#ifndef __eigenpy_eigen_to_python_hpp__ +#define __eigenpy_eigen_to_python_hpp__ + +#include "eigenpy/fwd.hpp" +#include "eigenpy/numpy-type.hpp" +#include "eigenpy/eigen-allocator.hpp" +#include "eigenpy/numpy-allocator.hpp" + +#include <boost/type_traits.hpp> + +namespace boost { namespace python { + + template<typename MatrixRef, class MakeHolder> + struct to_python_indirect_eigen + { + template <class U> + inline PyObject* operator()(U const& mat) const + { + return eigenpy::EigenToPy<MatrixRef>::convert(const_cast<U&>(mat)); + } + +#ifndef BOOST_PYTHON_NO_PY_SIGNATURES + inline PyTypeObject const* + get_pytype()const + { + return converter::registered_pytype<MatrixRef>::get_pytype(); + } +#endif + }; + + template <typename Scalar, int RowsAtCompileTime, int ColsAtCompileTime, int Options, int MaxRowsAtCompileTime, int MaxColsAtCompileTime, class MakeHolder> + struct to_python_indirect<Eigen::Matrix<Scalar,RowsAtCompileTime,ColsAtCompileTime,Options,MaxRowsAtCompileTime,MaxColsAtCompileTime>&,MakeHolder> + : to_python_indirect_eigen<Eigen::Matrix<Scalar,RowsAtCompileTime,ColsAtCompileTime,Options,MaxRowsAtCompileTime,MaxColsAtCompileTime>&,MakeHolder> + { + }; + + template <typename Scalar, int RowsAtCompileTime, int ColsAtCompileTime, int Options, int MaxRowsAtCompileTime, int MaxColsAtCompileTime, class MakeHolder> + struct to_python_indirect<const Eigen::Matrix<Scalar,RowsAtCompileTime,ColsAtCompileTime,Options,MaxRowsAtCompileTime,MaxColsAtCompileTime>&,MakeHolder> + : to_python_indirect_eigen<const Eigen::Matrix<Scalar,RowsAtCompileTime,ColsAtCompileTime,Options,MaxRowsAtCompileTime,MaxColsAtCompileTime>&,MakeHolder> + { + }; + +}} + +namespace eigenpy +{ + namespace bp = boost::python; + + template<typename MatType> + struct EigenToPy + { + static PyObject* convert(typename boost::add_reference<typename boost::add_const<MatType>::type>::type mat) + { + typedef typename boost::remove_const<typename boost::remove_reference<MatType>::type>::type MatrixDerived; + + assert( (mat.rows()<INT_MAX) && (mat.cols()<INT_MAX) + && "Matrix range larger than int ... should never happen." ); + const npy_intp R = (npy_intp)mat.rows(), C = (npy_intp)mat.cols(); + + PyArrayObject* pyArray; + // Allocate Python memory + if( ( ((!(C == 1) != !(R == 1)) && !MatrixDerived::IsVectorAtCompileTime) || MatrixDerived::IsVectorAtCompileTime) + && NumpyType::getType() == ARRAY_TYPE) // Handle array with a single dimension + { + npy_intp shape[1] = { C == 1 ? R : C }; + pyArray = NumpyAllocator<MatType>::allocate(const_cast<MatrixDerived &>(mat.derived()), + 1,shape); + } + else + { + npy_intp shape[2] = { R,C }; + pyArray = NumpyAllocator<MatType>::allocate(const_cast<MatrixDerived &>(mat.derived()), + 2,shape); + } + + // Create an instance (either np.array or np.matrix) + return NumpyType::make(pyArray).ptr(); + } + }; + + template<typename MatType> + struct EigenToPyConverter + { + static void registration() + { + bp::to_python_converter<MatType,EigenToPy<MatType> >(); + } + }; + +#if EIGEN_VERSION_AT_LEAST(3,2,0) + template<typename MatType> + struct EigenToPyConverter< eigenpy::Ref<MatType> > + { + static void registration() + { + } + }; +#endif +} + +#endif // __eigenpy_eigen_to_python_hpp__ diff --git a/include/eigenpy/eigenpy.hpp b/include/eigenpy/eigenpy.hpp index b9668da4dc930d6354b67b41b5670738608f8401..75c2c73ef927be1369ab4f4f8c68133e029eabe7 100644 --- a/include/eigenpy/eigenpy.hpp +++ b/include/eigenpy/eigenpy.hpp @@ -24,28 +24,28 @@ #endif // if EIGEN_VERSION_AT_LEAST(3,2,0) -#define EIGENPY_MAKE_TYPEDEFS(Type, TypeSuffix, Size, SizeSuffix) \ +#define EIGENPY_MAKE_TYPEDEFS(Type, Options, TypeSuffix, Size, SizeSuffix) \ /** \ingroup matrixtypedefs */ \ - typedef Eigen::Matrix<Type, Size, Size> Matrix##SizeSuffix##TypeSuffix; \ + typedef Eigen::Matrix<Type, Size, Size, Options> Matrix##SizeSuffix##TypeSuffix; \ /** \ingroup matrixtypedefs */ \ typedef Eigen::Matrix<Type, Size, 1> Vector##SizeSuffix##TypeSuffix; \ /** \ingroup matrixtypedefs */ \ typedef Eigen::Matrix<Type, 1, Size> RowVector##SizeSuffix##TypeSuffix; -#define EIGENPY_MAKE_FIXED_TYPEDEFS(Type, TypeSuffix, Size) \ +#define EIGENPY_MAKE_FIXED_TYPEDEFS(Type, Options, TypeSuffix, Size) \ /** \ingroup matrixtypedefs */ \ - typedef Eigen::Matrix<Type, Size, Eigen::Dynamic> Matrix##Size##X##TypeSuffix; \ + typedef Eigen::Matrix<Type, Size, Eigen::Dynamic, Options> Matrix##Size##X##TypeSuffix; \ /** \ingroup matrixtypedefs */ \ - typedef Eigen::Matrix<Type, Eigen::Dynamic, Size> Matrix##X##Size##TypeSuffix; + typedef Eigen::Matrix<Type, Eigen::Dynamic, Size, Options> Matrix##X##Size##TypeSuffix; -#define EIGENPY_MAKE_TYPEDEFS_ALL_SIZES(Type, TypeSuffix) \ - EIGENPY_MAKE_TYPEDEFS(Type, TypeSuffix, 2, 2) \ - EIGENPY_MAKE_TYPEDEFS(Type, TypeSuffix, 3, 3) \ - EIGENPY_MAKE_TYPEDEFS(Type, TypeSuffix, 4, 4) \ - EIGENPY_MAKE_TYPEDEFS(Type, TypeSuffix, Eigen::Dynamic, X) \ - EIGENPY_MAKE_FIXED_TYPEDEFS(Type, TypeSuffix, 2) \ - EIGENPY_MAKE_FIXED_TYPEDEFS(Type, TypeSuffix, 3) \ - EIGENPY_MAKE_FIXED_TYPEDEFS(Type, TypeSuffix, 4) +#define EIGENPY_MAKE_TYPEDEFS_ALL_SIZES(Type, Options, TypeSuffix) \ + EIGENPY_MAKE_TYPEDEFS(Type, Options, TypeSuffix, 2, 2) \ + EIGENPY_MAKE_TYPEDEFS(Type, Options, TypeSuffix, 3, 3) \ + EIGENPY_MAKE_TYPEDEFS(Type, Options, TypeSuffix, 4, 4) \ + EIGENPY_MAKE_TYPEDEFS(Type, Options, TypeSuffix, Eigen::Dynamic, X) \ + EIGENPY_MAKE_FIXED_TYPEDEFS(Type, Options, TypeSuffix, 2) \ + EIGENPY_MAKE_FIXED_TYPEDEFS(Type, Options, TypeSuffix, 3) \ + EIGENPY_MAKE_FIXED_TYPEDEFS(Type, Options, TypeSuffix, 4) namespace eigenpy { @@ -65,10 +65,10 @@ namespace eigenpy template<typename MatType,typename EigenEquivalentType> EIGENPY_DEPRECATED void enableEigenPySpecific(); - template<typename Scalar> + template<typename Scalar, int Options> EIGEN_DONT_INLINE void exposeType() { - EIGENPY_MAKE_TYPEDEFS_ALL_SIZES(Scalar,s); + EIGENPY_MAKE_TYPEDEFS_ALL_SIZES(Scalar,Options,s); ENABLE_SPECIFIC_MATRIX_TYPE(Vector2s); ENABLE_SPECIFIC_MATRIX_TYPE(RowVector2s); @@ -93,6 +93,12 @@ namespace eigenpy ENABLE_SPECIFIC_MATRIX_TYPE(MatrixXs); } + template<typename Scalar> + EIGEN_DONT_INLINE void exposeType() + { + exposeType<Scalar,0>(); + } + } // namespace eigenpy #include "eigenpy/details.hpp" diff --git a/include/eigenpy/fwd.hpp b/include/eigenpy/fwd.hpp index 702b7d435254205003bf797445bf223b632d9f77..3bc5937d05caebcd8be7c367693144acb31364b1 100644 --- a/include/eigenpy/fwd.hpp +++ b/include/eigenpy/fwd.hpp @@ -1,6 +1,5 @@ /* - * Copyright 2014-2019, CNRS - * Copyright 2018-2019, INRIA + * Copyright 2014-2020 CNRS INRIA */ #ifndef __eigenpy_fwd_hpp__ @@ -11,12 +10,9 @@ #include <boost/python.hpp> #include <Eigen/Core> -#include <numpy/numpyconfig.h> -#ifdef NPY_1_8_API_VERSION -#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION -#endif - -#include <numpy/noprefix.h> +#define NO_IMPORT_ARRAY + #include "eigenpy/numpy.hpp" +#undef NO_IMPORT_ARRAY #ifdef NPY_ALIGNED #if EIGEN_VERSION_AT_LEAST(3,2,90) @@ -30,5 +26,10 @@ #include "eigenpy/expose.hpp" -#endif // ifndef __eigenpy_fwd_hpp__ +namespace eigenpy +{ + template<typename MatType> struct EigenToPy; + template<typename MatType> struct EigenFromPy; +} +#endif // ifndef __eigenpy_fwd_hpp__ diff --git a/include/eigenpy/map.hpp b/include/eigenpy/map.hpp index db5ffc89ffb85376d8df9b9a72d68416b211131a..487506cd0116e01b23fa622a223e737c8281dac0 100644 --- a/include/eigenpy/map.hpp +++ b/include/eigenpy/map.hpp @@ -7,7 +7,6 @@ #define __eigenpy_map_hpp__ #include "eigenpy/fwd.hpp" -#include <numpy/arrayobject.h> #include "eigenpy/exception.hpp" #include "eigenpy/stride.hpp" diff --git a/include/eigenpy/numpy-allocator.hpp b/include/eigenpy/numpy-allocator.hpp new file mode 100644 index 0000000000000000000000000000000000000000..02a74215abe2dd198877dcb9c79b759eea33cff3 --- /dev/null +++ b/include/eigenpy/numpy-allocator.hpp @@ -0,0 +1,74 @@ +/* + * Copyright 2020 INRIA + */ + +#ifndef __eigenpy_numpy_allocator_hpp__ +#define __eigenpy_numpy_allocator_hpp__ + +#include "eigenpy/fwd.hpp" +#include "eigenpy/numpy-type.hpp" +#include "eigenpy/eigen-allocator.hpp" + +namespace eigenpy +{ + template<typename MatType> + struct NumpyAllocator + { + template<typename SimilarMatrixType> + static PyArrayObject * allocate(const Eigen::MatrixBase<SimilarMatrixType> & mat, + npy_intp nd, npy_intp * shape) + { + typedef typename SimilarMatrixType::Scalar Scalar; + + PyArrayObject * pyArray = (PyArrayObject*) PyArray_SimpleNew(nd, shape, + NumpyEquivalentType<Scalar>::type_code); + + // Copy data + EigenAllocator<SimilarMatrixType>::copy(mat,pyArray); + + return pyArray; + } + }; + + template<typename MatType> + struct NumpyAllocator<MatType &> + { + template<typename SimilarMatrixType> + static PyArrayObject * allocate(Eigen::PlainObjectBase<SimilarMatrixType> & mat, + npy_intp nd, npy_intp * shape) + { + typedef typename SimilarMatrixType::Scalar Scalar; + enum { NPY_ARRAY_MEMORY_CONTIGUOUS = SimilarMatrixType::IsRowMajor ? NPY_ARRAY_CARRAY : NPY_ARRAY_FARRAY }; + + PyArrayObject * pyArray = (PyArrayObject*) PyArray_New(&PyArray_Type, nd, shape, + NumpyEquivalentType<Scalar>::type_code, NULL, + mat.data(), 0, + NPY_ARRAY_MEMORY_CONTIGUOUS | NPY_ARRAY_ALIGNED, + NULL); + + return pyArray; + } + }; + + template<typename MatType> + struct NumpyAllocator<const MatType &> + { + template<typename SimilarMatrixType> + static PyArrayObject * allocate(const Eigen::PlainObjectBase<SimilarMatrixType> & mat, + npy_intp nd, npy_intp * shape) + { + typedef typename SimilarMatrixType::Scalar Scalar; + enum { NPY_ARRAY_MEMORY_CONTIGUOUS_RO = SimilarMatrixType::IsRowMajor ? NPY_ARRAY_CARRAY_RO : NPY_ARRAY_FARRAY_RO }; + + PyArrayObject * pyArray = (PyArrayObject*) PyArray_New(&PyArray_Type, nd, shape, + NumpyEquivalentType<Scalar>::type_code, NULL, + const_cast<SimilarMatrixType &>(mat.derived()).data(), 0, + NPY_ARRAY_MEMORY_CONTIGUOUS_RO | NPY_ARRAY_ALIGNED, + NULL); + + return pyArray; + } + }; +} + +#endif // ifndef __eigenpy_numpy_allocator_hpp__ diff --git a/include/eigenpy/numpy-type.hpp b/include/eigenpy/numpy-type.hpp index 07be4c99a252a54373c31207d33387d0b39511aa..2bf7d03b9737563d873bbaea8600d90fb6eff4c0 100644 --- a/include/eigenpy/numpy-type.hpp +++ b/include/eigenpy/numpy-type.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2018-2020, INRIA + * Copyright 2018-2020 INRIA */ #ifndef __eigenpy_numpy_type_hpp__ @@ -7,12 +7,22 @@ #include "eigenpy/fwd.hpp" -#include <iostream> #include <patchlevel.h> // For PY_MAJOR_VERSION namespace eigenpy { namespace bp = boost::python; + + template <typename SCALAR> struct NumpyEquivalentType {}; + + template <> struct NumpyEquivalentType<float> { enum { type_code = NPY_FLOAT };}; + template <> struct NumpyEquivalentType< std::complex<float> > { enum { type_code = NPY_CFLOAT };}; + template <> struct NumpyEquivalentType<double> { enum { type_code = NPY_DOUBLE };}; + template <> struct NumpyEquivalentType< std::complex<double> > { enum { type_code = NPY_CDOUBLE };}; + template <> struct NumpyEquivalentType<long double> { enum { type_code = NPY_LONGDOUBLE };}; + template <> struct NumpyEquivalentType< std::complex<long double> > { enum { type_code = NPY_CLONGDOUBLE };}; + template <> struct NumpyEquivalentType<int> { enum { type_code = NPY_INT };}; + template <> struct NumpyEquivalentType<long> { enum { type_code = NPY_LONG };}; enum NP_TYPE { @@ -102,9 +112,11 @@ namespace eigenpy } protected: + NumpyType() { pyModule = bp::import("numpy"); + #if PY_MAJOR_VERSION >= 3 // TODO I don't know why this Py_INCREF is necessary. // Without it, the destructor of NumpyType SEGV sometimes. @@ -132,7 +144,6 @@ namespace eigenpy NP_TYPE np_type; }; - } #endif // ifndef __eigenpy_numpy_type_hpp__ diff --git a/include/eigenpy/numpy.hpp b/include/eigenpy/numpy.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c8780a2c4e7beb2c4627a633bc802d408a23404f --- /dev/null +++ b/include/eigenpy/numpy.hpp @@ -0,0 +1,29 @@ +/* + * Copyright 2020 INRIA + */ + +#ifndef __eigenpy_numpy_hpp__ +#define __eigenpy_numpy_hpp__ + +#include <boost/python.hpp> +#include "eigenpy/config.hpp" + +#ifndef PY_ARRAY_UNIQUE_SYMBOL + #define PY_ARRAY_UNIQUE_SYMBOL EIGENPY_ARRAY_API +#endif + +#include <numpy/numpyconfig.h> +#ifdef NPY_1_8_API_VERSION + #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION +#endif + +#include <numpy/noprefix.h> + +#define EIGENPY_GET_PY_ARRAY_TYPE(array) PyArray_ObjectType(reinterpret_cast<PyObject *>(array), 0) + +namespace eigenpy +{ + void EIGENPY_DLLEXPORT import_numpy(); +} + +#endif // ifndef __eigenpy_numpy_hpp__ diff --git a/include/eigenpy/scalar-conversion.hpp b/include/eigenpy/scalar-conversion.hpp new file mode 100644 index 0000000000000000000000000000000000000000..b960068af63dc2da18f77942666d129cac320bc1 --- /dev/null +++ b/include/eigenpy/scalar-conversion.hpp @@ -0,0 +1,44 @@ +// +// Copyright (c) 2014-2020 CNRS INRIA +// + +#ifndef __eigenpy_scalar_conversion_hpp__ +#define __eigenpy_scalar_conversion_hpp__ + +#include "eigenpy/config.hpp" + +namespace eigenpy +{ + template <typename SCALAR1, typename SCALAR2> + struct FromTypeToType : public boost::false_type {}; + + template <typename SCALAR> + struct FromTypeToType<SCALAR,SCALAR> : public boost::true_type {}; + + template <> struct FromTypeToType<int,long> : public boost::true_type {}; + template <> struct FromTypeToType<int,float> : public boost::true_type {}; + template <> struct FromTypeToType<int,std::complex<float> > : public boost::true_type {}; + template <> struct FromTypeToType<int,double> : public boost::true_type {}; + template <> struct FromTypeToType<int,std::complex<double> > : public boost::true_type {}; + template <> struct FromTypeToType<int,long double> : public boost::true_type {}; + template <> struct FromTypeToType<int,std::complex<long double> > : public boost::true_type {}; + + template <> struct FromTypeToType<long,float> : public boost::true_type {}; + template <> struct FromTypeToType<long,std::complex<float> > : public boost::true_type {}; + template <> struct FromTypeToType<long,double> : public boost::true_type {}; + template <> struct FromTypeToType<long,std::complex<double> > : public boost::true_type {}; + template <> struct FromTypeToType<long,long double> : public boost::true_type {}; + template <> struct FromTypeToType<long,std::complex<long double> > : public boost::true_type {}; + + template <> struct FromTypeToType<float,std::complex<float> > : public boost::true_type {}; + template <> struct FromTypeToType<float,double> : public boost::true_type {}; + template <> struct FromTypeToType<float,std::complex<double> > : public boost::true_type {}; + template <> struct FromTypeToType<float,long double> : public boost::true_type {}; + template <> struct FromTypeToType<float,std::complex<long double> > : public boost::true_type {}; + + template <> struct FromTypeToType<double,std::complex<double> > : public boost::true_type {}; + template <> struct FromTypeToType<double,long double> : public boost::true_type {}; + template <> struct FromTypeToType<double,std::complex<long double> > : public boost::true_type {}; +} + +#endif // __eigenpy_scalar_conversion_hpp__ diff --git a/include/eigenpy/version.hpp b/include/eigenpy/version.hpp index 1d0b98f52d34e695c15024989e5b0ad6e00a28f4..ac98007d65dc748e3b63e48e1e81c7597a42a13f 100644 --- a/include/eigenpy/version.hpp +++ b/include/eigenpy/version.hpp @@ -32,8 +32,8 @@ namespace eigenpy /// by the input arguments. /// bool EIGENPY_DLLEXPORT checkVersionAtLeast(unsigned int major_version, - unsigned int minor_version, - unsigned int patch_version); + unsigned int minor_version, + unsigned int patch_version); } #endif // __eigenpy_version_hpp__ diff --git a/src/eigenpy.cpp b/src/eigenpy.cpp index c85030b1ff9d87a689ec609635f5caba42f39fb5..c2f49067d105f24fb919f7a5cfa0c510e0b9e58f 100644 --- a/src/eigenpy.cpp +++ b/src/eigenpy.cpp @@ -6,7 +6,6 @@ #include "eigenpy/eigenpy.hpp" #include <stdlib.h> - namespace eigenpy { @@ -29,6 +28,7 @@ namespace eigenpy void enableEigenPy() { using namespace Eigen; + import_numpy(); Exception::registerException(); diff --git a/src/matrix-complex-double.cpp b/src/matrix-complex-double.cpp index 899f88fcc7ba53faf94f577fcd5c02844ef74e79..a3fa16341aed82f091b67a5a4975aa52e8e2b8a7 100644 --- a/src/matrix-complex-double.cpp +++ b/src/matrix-complex-double.cpp @@ -9,5 +9,6 @@ namespace eigenpy void exposeMatrixComplexDouble() { exposeType<std::complex<double> >(); + exposeType<std::complex<double>,Eigen::RowMajor>(); } } diff --git a/src/matrix-complex-float.cpp b/src/matrix-complex-float.cpp index 323c52d761d71c4427e610f40f3679409ad87343..885d2a3e856fa633e5b05b3045fa511b725e6889 100644 --- a/src/matrix-complex-float.cpp +++ b/src/matrix-complex-float.cpp @@ -9,5 +9,6 @@ namespace eigenpy void exposeMatrixComplexFloat() { exposeType<std::complex<float> >(); + exposeType<std::complex<float>,Eigen::RowMajor>(); } } diff --git a/src/matrix-complex-long-double.cpp b/src/matrix-complex-long-double.cpp index 1f987ae10ef913941d9a34d70362f7f11ae41779..b27cf1dd537847969a7d41aa109cbf9ee08e383e 100644 --- a/src/matrix-complex-long-double.cpp +++ b/src/matrix-complex-long-double.cpp @@ -9,5 +9,6 @@ namespace eigenpy void exposeMatrixComplexLongDouble() { exposeType<std::complex<long double> >(); + exposeType<std::complex<long double>,Eigen::RowMajor>(); } } diff --git a/src/matrix-double.cpp b/src/matrix-double.cpp index dc84a9c257af26261b39093872dda7c4acb70a30..13215978ac3894939b7cc6acce388abac5078255 100644 --- a/src/matrix-double.cpp +++ b/src/matrix-double.cpp @@ -9,5 +9,6 @@ namespace eigenpy void exposeMatrixDouble() { exposeType<double>(); + exposeType<double,Eigen::RowMajor>(); } } diff --git a/src/matrix-float.cpp b/src/matrix-float.cpp index 100b8e9ee77799a0d0cb8e4484abced47a5e3f78..44779560048a561c5b5c2a5b6626d45e99674825 100644 --- a/src/matrix-float.cpp +++ b/src/matrix-float.cpp @@ -9,5 +9,6 @@ namespace eigenpy void exposeMatrixFloat() { exposeType<float>(); + exposeType<float,Eigen::RowMajor>(); } } diff --git a/src/matrix-int.cpp b/src/matrix-int.cpp index a4b0d8b2f8f1803350330df7558ca7e764032e5c..476746cc94b7f831f2f49bd3f805f720401941c2 100644 --- a/src/matrix-int.cpp +++ b/src/matrix-int.cpp @@ -9,5 +9,6 @@ namespace eigenpy void exposeMatrixInt() { exposeType<int>(); + exposeType<int,Eigen::RowMajor>(); } } diff --git a/src/matrix-long-double.cpp b/src/matrix-long-double.cpp index 1b2a8f71eed54fa5620ff446e8b000f29a7499ce..41bcb69957aefb167b855e9e53e365611b3d7fc4 100644 --- a/src/matrix-long-double.cpp +++ b/src/matrix-long-double.cpp @@ -9,5 +9,6 @@ namespace eigenpy void exposeMatrixLongDouble() { exposeType<long double>(); + exposeType<long double,Eigen::RowMajor>(); } } diff --git a/src/matrix-long.cpp b/src/matrix-long.cpp index 835b6cce0c227305b4a0e203cedfed5f04a56a9e..6d34fcef20d93d0f312012cfbf03889462ae167e 100644 --- a/src/matrix-long.cpp +++ b/src/matrix-long.cpp @@ -9,5 +9,6 @@ namespace eigenpy void exposeMatrixLong() { exposeType<long>(); + exposeType<long,Eigen::RowMajor>(); } } diff --git a/src/numpy.cpp b/src/numpy.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4c76caef62ce2d8e7a14f5a2c7a06f14f081e4b0 --- /dev/null +++ b/src/numpy.cpp @@ -0,0 +1,18 @@ +/* + * Copyright 2020 INRIA + */ + +#include "eigenpy/numpy.hpp" + +namespace eigenpy +{ + void import_numpy() + { + if(_import_array() < 0) + { + PyErr_Print(); + PyErr_SetString(PyExc_ImportError, "numpy.core.multiarray failed to import"); + } + // std::cout << "init _import_array " << std::endl; + } +} diff --git a/unittest/CMakeLists.txt b/unittest/CMakeLists.txt index 03d0997e78d6ccc4c4ead4556fba9ad43b3f8c78..17bf479fe818ee27c9e0997c626b4399b797b4ef 100644 --- a/unittest/CMakeLists.txt +++ b/unittest/CMakeLists.txt @@ -32,6 +32,7 @@ ENDMACRO(ADD_LIB_UNIT_TEST) ADD_LIB_UNIT_TEST(matrix "eigen3") ADD_LIB_UNIT_TEST(geometry "eigen3") ADD_LIB_UNIT_TEST(complex "eigen3") +ADD_LIB_UNIT_TEST(return_by_ref "eigen3") IF(NOT ${EIGEN3_VERSION} VERSION_LESS "3.2.0") ADD_LIB_UNIT_TEST(ref "eigen3") ENDIF() @@ -39,6 +40,7 @@ ENDIF() ADD_PYTHON_UNIT_TEST("py-matrix" "unittest/python/test_matrix.py" "unittest") ADD_PYTHON_UNIT_TEST("py-geometry" "unittest/python/test_geometry.py" "unittest") ADD_PYTHON_UNIT_TEST("py-complex" "unittest/python/test_complex.py" "unittest") +ADD_PYTHON_UNIT_TEST("py-return-by-ref" "unittest/python/test_return_by_ref.py" "unittest") ADD_PYTHON_UNIT_TEST("py-switch" "unittest/python/test_switch.py" "python/eigenpy") SET_TESTS_PROPERTIES("py-switch" PROPERTIES DEPENDS ${PYWRAP}) diff --git a/unittest/python/test_return_by_ref.py b/unittest/python/test_return_by_ref.py new file mode 100644 index 0000000000000000000000000000000000000000..969f84672335af20ec65b1d91cb64e26c17f1830 --- /dev/null +++ b/unittest/python/test_return_by_ref.py @@ -0,0 +1,34 @@ +from return_by_ref import Matrix, RowMatrix, Vector +import numpy as np + +def test(mat): + + m_ref = mat.ref() + m_ref.fill(0) + m_copy = mat.copy() + assert np.array_equal(m_ref,m_copy) + + m_const_ref = mat.const_ref() + assert np.array_equal(m_const_ref,m_copy) + assert np.array_equal(m_const_ref,m_ref) + + m_ref.fill(1) + assert not np.array_equal(m_ref,m_copy) + assert np.array_equal(m_const_ref,m_ref) + + try: + m_const_ref.fill(2) + assert False + except: + assert True + +rows = 10 +cols = 30 + +mat = Matrix(rows,cols) +row_mat = RowMatrix(rows,cols) +vec = Vector(rows,1) + +test(mat) +test(row_mat) +test(vec) diff --git a/unittest/return_by_ref.cpp b/unittest/return_by_ref.cpp new file mode 100644 index 0000000000000000000000000000000000000000..45d5c46e157c9b49d877acb87e90a13196056c94 --- /dev/null +++ b/unittest/return_by_ref.cpp @@ -0,0 +1,56 @@ +/* + * Copyright 2020 INRIA + */ + +#include "eigenpy/eigenpy.hpp" +#include <iostream> + +template<typename Matrix> +struct Base +{ + Base(const Eigen::DenseIndex rows, + const Eigen::DenseIndex cols) + : mat(rows,cols) + {} + + void show() + { + std::cout << mat << std::endl; + } + + Matrix & ref() { return mat; } + const Matrix & const_ref() { return mat; } + Matrix copy() { return mat; } + +protected: + + Matrix mat; +}; + +template<typename MatrixType> +void expose_matrix_class(const std::string & name) +{ + using namespace Eigen; + namespace bp = boost::python; + + bp::class_<Base<MatrixType> >(name.c_str(),bp::init<DenseIndex,DenseIndex>()) + .def("show",&Base<MatrixType>::show) + .def("ref",&Base<MatrixType>::ref, bp::return_internal_reference<>()) + .def("const_ref",&Base<MatrixType>::const_ref, bp::return_internal_reference<>()) + .def("copy",&Base<MatrixType>::copy); +} + +BOOST_PYTHON_MODULE(return_by_ref) +{ + using namespace Eigen; + eigenpy::enableEigenPy(); + + typedef Eigen::Matrix<double,Eigen::Dynamic,1> VectorType; + typedef Eigen::Matrix<double,Eigen::Dynamic,Eigen::Dynamic> MatrixType; + typedef Eigen::Matrix<double,Eigen::Dynamic,Eigen::Dynamic,Eigen::RowMajor> RowMatrixType; + + expose_matrix_class<VectorType>("Vector"); + expose_matrix_class<MatrixType>("Matrix"); + expose_matrix_class<RowMatrixType>("RowMatrix"); +} +