diff --git a/CMakeLists.txt b/CMakeLists.txt index d65f9d58d61537465e07df578644411cc9f12502..ea73ef4086d952dbaf28bc81230432739a50b036 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,6 +74,7 @@ SEARCH_FOR_BOOST() SET(${PROJECT_NAME}_UTILS_HEADERS include/eigenpy/utils/scalar-name.hpp include/eigenpy/utils/is-approx.hpp + include/eigenpy/utils/is-aligned.hpp ) SET(${PROJECT_NAME}_SOLVERS_HEADERS diff --git a/include/eigenpy/eigen-allocator.hpp b/include/eigenpy/eigen-allocator.hpp index 243c1d3d4d1ba870c965af770cd11434cbf5b499..1a104a620898b89accb53409438b2cfcffd78022 100644 --- a/include/eigenpy/eigen-allocator.hpp +++ b/include/eigenpy/eigen-allocator.hpp @@ -8,6 +8,7 @@ #include "eigenpy/fwd.hpp" #include "eigenpy/map.hpp" #include "eigenpy/scalar-conversion.hpp" +#include "eigenpy/utils/is-aligned.hpp" namespace eigenpy { @@ -17,7 +18,7 @@ namespace eigenpy template<typename MatType, bool IsVectorAtCompileTime = MatType::IsVectorAtCompileTime> struct init_matrix_or_array { - static MatType * run(PyArrayObject * pyArray, void * storage) + static MatType * run(PyArrayObject * pyArray, void * storage = NULL) { assert(PyArray_NDIM(pyArray) == 1 || PyArray_NDIM(pyArray) == 2); @@ -33,25 +34,34 @@ namespace eigenpy cols = 1; } - return new (storage) MatType(rows,cols); + if(storage) + return new (storage) MatType(rows,cols); + else + return new MatType(rows,cols); } }; template<typename MatType> struct init_matrix_or_array<MatType,true> { - static MatType * run(PyArrayObject * pyArray, void * storage) + static MatType * run(PyArrayObject * pyArray, void * storage = NULL) { if(PyArray_NDIM(pyArray) == 1) { const int rows_or_cols = (int)PyArray_DIMS(pyArray)[0]; - return new (storage) MatType(rows_or_cols); + if(storage) + return new (storage) MatType(rows_or_cols); + else + return new 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); + if(storage) + return new (storage) MatType(rows,cols); + else + return new MatType(rows,cols); } } }; @@ -79,7 +89,7 @@ namespace eigenpy const Eigen::MatrixBase<MatrixOut> & /*dest*/) { // do nothing - assert("Must never happened"); + assert(false && "Must never happened"); } }; @@ -97,9 +107,11 @@ namespace eigenpy typedef MatType Type; typedef typename MatType::Scalar Scalar; - static void allocate(PyArrayObject * pyArray, void * storage) + static void allocate(PyArrayObject * pyArray, + bp::converter::rvalue_from_python_storage<MatType> * storage) { - Type * mat_ptr = details::init_matrix_or_array<Type>::run(pyArray,storage); + void * raw_ptr = storage->storage.bytes; + Type * mat_ptr = details::init_matrix_or_array<Type>::run(pyArray,raw_ptr); Type & mat = *mat_ptr; const int pyArray_Type = EIGENPY_GET_PY_ARRAY_TYPE(pyArray); @@ -193,6 +205,190 @@ namespace eigenpy }; #if EIGEN_VERSION_AT_LEAST(3,2,0) + template<typename MatType, int Options, typename Stride> + struct EigenAllocator<Eigen::Ref<MatType,Options,Stride> > + { + typedef Eigen::Ref<MatType,Options,Stride> RefType; + typedef typename MatType::Scalar Scalar; + + typedef typename ::boost::python::detail::referent_storage<RefType&>::StorageType StorageType; + + static void allocate(PyArrayObject * pyArray, + bp::converter::rvalue_from_python_storage<RefType> * storage) + { + typedef typename StrideType<MatType,Eigen::internal::traits<RefType>::StrideType::InnerStrideAtCompileTime, Eigen::internal::traits<RefType>::StrideType::OuterStrideAtCompileTime >::type NumpyMapStride; + + bool need_to_allocate = false; + const int pyArray_Type = EIGENPY_GET_PY_ARRAY_TYPE(pyArray); + if(pyArray_Type != NumpyEquivalentType<Scalar>::type_code) + need_to_allocate |= true; + if( (MatType::IsRowMajor && (PyArray_IS_C_CONTIGUOUS(pyArray) && !PyArray_IS_F_CONTIGUOUS(pyArray))) + || (!MatType::IsRowMajor && (PyArray_IS_F_CONTIGUOUS(pyArray) && !PyArray_IS_C_CONTIGUOUS(pyArray))) + || MatType::IsVectorAtCompileTime + || (PyArray_IS_F_CONTIGUOUS(pyArray) && PyArray_IS_C_CONTIGUOUS(pyArray))) // no need to allocate + need_to_allocate |= false; + else + need_to_allocate |= true; + if(Options != Eigen::Unaligned) // we need to check whether the memory is correctly aligned and composed of a continuous segment + { + void * data_ptr = PyArray_DATA(pyArray); + if(!PyArray_ISONESEGMENT(pyArray) || !is_aligned(data_ptr,Options)) + need_to_allocate |= true; + } + + void * raw_ptr = storage->storage.bytes; + if(need_to_allocate) + { + MatType * mat_ptr; + mat_ptr = details::init_matrix_or_array<MatType>::run(pyArray); + RefType mat_ref(*mat_ptr); + + new (raw_ptr) StorageType(mat_ref,pyArray,mat_ptr); + + RefType & mat = *reinterpret_cast<RefType*>(raw_ptr); + 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."); + } + } + else + { + assert(pyArray_Type == NumpyEquivalentType<Scalar>::type_code); + typename MapNumpy<MatType,Scalar,Options,NumpyMapStride>::EigenMap numpyMap = MapNumpy<MatType,Scalar,Options,NumpyMapStride>::map(pyArray); + RefType mat_ref(numpyMap); + new (raw_ptr) StorageType(mat_ref,pyArray); + } + } + + static void copy(RefType const & ref, PyArrayObject * pyArray) + { + EigenAllocator<MatType>::copy(ref,pyArray); + } + }; + + template<typename MatType, int Options, typename Stride> + struct EigenAllocator<const Eigen::Ref<const MatType,Options,Stride> > + { + typedef const Eigen::Ref<const MatType,Options,Stride> RefType; + typedef typename MatType::Scalar Scalar; + + typedef typename ::boost::python::detail::referent_storage<RefType&>::StorageType StorageType; + + static void allocate(PyArrayObject * pyArray, + bp::converter::rvalue_from_python_storage<RefType> * storage) + { + typedef typename StrideType<MatType,Eigen::internal::traits<RefType>::StrideType::InnerStrideAtCompileTime, Eigen::internal::traits<RefType>::StrideType::OuterStrideAtCompileTime >::type NumpyMapStride; + + bool need_to_allocate = false; + const int pyArray_Type = EIGENPY_GET_PY_ARRAY_TYPE(pyArray); + if(pyArray_Type != NumpyEquivalentType<Scalar>::type_code) + need_to_allocate |= true; + if( (MatType::IsRowMajor && (PyArray_IS_C_CONTIGUOUS(pyArray) && !PyArray_IS_F_CONTIGUOUS(pyArray))) + || (!MatType::IsRowMajor && (PyArray_IS_F_CONTIGUOUS(pyArray) && !PyArray_IS_C_CONTIGUOUS(pyArray))) + || MatType::IsVectorAtCompileTime + || (PyArray_IS_F_CONTIGUOUS(pyArray) && PyArray_IS_C_CONTIGUOUS(pyArray))) // no need to allocate + need_to_allocate |= false; + else + need_to_allocate |= true; + if(Options != Eigen::Unaligned) // we need to check whether the memory is correctly aligned and composed of a continuous segment + { + void * data_ptr = PyArray_DATA(pyArray); + if(!PyArray_ISONESEGMENT(pyArray) || !is_aligned(data_ptr,Options)) + need_to_allocate |= true; + } + + void * raw_ptr = storage->storage.bytes; + if(need_to_allocate) + { + MatType * mat_ptr; + mat_ptr = details::init_matrix_or_array<MatType>::run(pyArray); + RefType mat_ref(*mat_ptr); + + new (raw_ptr) StorageType(mat_ref,pyArray,mat_ptr); + + MatType & mat = *mat_ptr; + 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."); + } + } + else + { + assert(pyArray_Type == NumpyEquivalentType<Scalar>::type_code); + typename MapNumpy<MatType,Scalar,Options,NumpyMapStride>::EigenMap numpyMap = MapNumpy<MatType,Scalar,Options,NumpyMapStride>::map(pyArray); + RefType mat_ref(numpyMap); + new (raw_ptr) StorageType(mat_ref,pyArray); + } + } + + static void copy(RefType const & ref, PyArrayObject * pyArray) + { + EigenAllocator<MatType>::copy(ref,pyArray); + } + }; + template<typename MatType> struct EigenAllocator< eigenpy::Ref<MatType> > { diff --git a/include/eigenpy/eigen-from-python.hpp b/include/eigenpy/eigen-from-python.hpp index c923079153e02cedf8eedd479955674927628e58..325326d6778bfdd743725b32a30e7efb527a726d 100644 --- a/include/eigenpy/eigen-from-python.hpp +++ b/include/eigenpy/eigen-from-python.hpp @@ -12,6 +12,103 @@ #include <boost/python/converter/rvalue_from_python_data.hpp> +namespace eigenpy +{ + namespace details + { + template<typename MatType, bool is_const = boost::is_const<MatType>::value> + struct copy_if_non_const + { + static void run(const Eigen::MatrixBase<MatType> & input, + PyArrayObject * pyArray) + { + EigenAllocator<MatType>::copy(input,pyArray); + } + }; + + template<typename MatType> + struct copy_if_non_const<const MatType,true> + { + static void run(const Eigen::MatrixBase<MatType> & /*input*/, + PyArrayObject * /*pyArray*/) + {} + }; + +#if EIGEN_VERSION_AT_LEAST(3,2,0) + template<typename MatType, int Options, typename Stride> struct referent_storage_eigen_ref; + + template<typename MatType, int Options, typename Stride> + struct referent_storage_eigen_ref + { + typedef Eigen::Ref<MatType,Options,Stride> RefType; + + typedef ::boost::python::detail::aligned_storage< + ::boost::python::detail::referent_size<RefType&>::value + > AlignedStorage; + + referent_storage_eigen_ref() + : pyArray(NULL) + , mat_ptr(NULL) + , ref_ptr(reinterpret_cast<RefType*>(ref_storage.bytes)) + { + } + + referent_storage_eigen_ref(const RefType & ref, + PyArrayObject * pyArray, + MatType * mat_ptr = NULL) + : pyArray(pyArray) + , mat_ptr(mat_ptr) + , ref_ptr(reinterpret_cast<RefType*>(ref_storage.bytes)) + { + Py_INCREF(pyArray); + new (ref_storage.bytes) RefType(ref); + } + + ~referent_storage_eigen_ref() + { + if(mat_ptr != NULL && PyArray_ISWRITEABLE(pyArray)) + copy_if_non_const<MatType>::run(*mat_ptr,pyArray); + + Py_DECREF(pyArray); + + if(mat_ptr != NULL) + mat_ptr->~MatType(); + + ref_ptr->~RefType(); + } + + AlignedStorage ref_storage; + PyArrayObject * pyArray; + MatType * mat_ptr; + RefType * ref_ptr; + }; +#endif + + } +} + +namespace boost { namespace python { namespace detail { +#if EIGEN_VERSION_AT_LEAST(3,2,0) + template<typename MatType, int Options, typename Stride> + struct referent_storage<Eigen::Ref<MatType,Options,Stride> &> + { + typedef ::eigenpy::details::referent_storage_eigen_ref<MatType,Options,Stride> StorageType; + typedef aligned_storage< + ::boost::python::detail::referent_size<StorageType&>::value + > type; + }; + + template<typename MatType, int Options, typename Stride> + struct referent_storage<const Eigen::Ref<const MatType,Options,Stride> &> + { + typedef ::eigenpy::details::referent_storage_eigen_ref<const MatType,Options,Stride> StorageType; + typedef aligned_storage< + ::boost::python::detail::referent_size<StorageType&>::value + > type; + }; +#endif +}}} + namespace boost { namespace python { namespace converter { template<typename MatrixReference> @@ -51,7 +148,6 @@ namespace boost { namespace python { namespace converter { } }; - #define RVALUE_FROM_PYTHON_DATA_INIT(type) \ typedef rvalue_from_python_data_eigen<type> Base; \ \ @@ -79,10 +175,100 @@ namespace boost { namespace python { namespace converter { #undef RVALUE_FROM_PYTHON_DATA_INIT + template<typename MatType, int Options, typename Stride> + struct rvalue_from_python_data<Eigen::Ref<MatType,Options,Stride> &> + : rvalue_from_python_storage<Eigen::Ref<MatType,Options,Stride> &> + { + typedef Eigen::Ref<MatType,Options,Stride> 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() + { + typedef ::eigenpy::details::referent_storage_eigen_ref<MatType, Options,Stride> StorageType; + if (this->stage1.convertible == this->storage.bytes) + static_cast<StorageType *>((void *)this->storage.bytes)->~StorageType(); + } + }; + + template<typename MatType, int Options, typename Stride> + struct rvalue_from_python_data<const Eigen::Ref<const MatType,Options,Stride> &> + : rvalue_from_python_storage<const Eigen::Ref<const MatType,Options,Stride> &> + { + typedef const Eigen::Ref<const MatType,Options,Stride> 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() + { + typedef ::eigenpy::details::referent_storage_eigen_ref<const MatType, Options,Stride> StorageType; + if (this->stage1.convertible == this->storage.bytes) + static_cast<StorageType *>((void *)this->storage.bytes)->~StorageType(); + } + }; + } } } namespace eigenpy { + + template<typename MatOrRefType> + void eigen_from_py_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)); + + bp::converter::rvalue_from_python_storage<MatOrRefType>* storage = reinterpret_cast<bp::converter::rvalue_from_python_storage<MatOrRefType>*> + (reinterpret_cast<void*>(memory)); + + EigenAllocator<MatOrRefType>::allocate(pyArray,storage); + + memory->convertible = storage->storage.bytes; + } + template<typename MatType> struct EigenFromPy { @@ -215,15 +401,7 @@ namespace eigenpy void EigenFromPy<MatType>::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; + eigen_from_py_construct<MatType>(pyObj,memory); } template<typename MatType> @@ -241,13 +419,23 @@ namespace eigenpy { EigenFromPy<MatType>::registration(); - // Add also conversion to Eigen::MatrixBase<MatType> + // Add conversion to Eigen::MatrixBase<MatType> typedef Eigen::MatrixBase<MatType> MatrixBase; EigenFromPy<MatrixBase>::registration(); - // Add also conversion to Eigen::EigenBase<MatType> + // Add conversion to Eigen::EigenBase<MatType> typedef Eigen::EigenBase<MatType> EigenBase; EigenFromPy<EigenBase>::registration(); + +#if EIGEN_VERSION_AT_LEAST(3,2,0) + // Add conversion to Eigen::Ref<MatType> + typedef Eigen::Ref<MatType> RefType; + EigenFromPy<RefType>::registration(); + + // Add conversion to Eigen::Ref<MatType> + typedef const Eigen::Ref<const MatType> ConstRefType; + EigenFromPy<ConstRefType>::registration(); +#endif } }; @@ -280,6 +468,51 @@ namespace eigenpy }; #if EIGEN_VERSION_AT_LEAST(3,2,0) + + template<typename MatType, int Options, typename Stride> + struct EigenFromPy<Eigen::Ref<MatType,Options,Stride> > + { + typedef Eigen::Ref<MatType,Options,Stride> RefType; + typedef typename MatType::Scalar Scalar; + + /// \brief Determine if pyObj can be converted into a MatType object + static void* convertible(PyArrayObject * pyArray) + { + if(!PyArray_Check(pyArray)) + return 0; + if(!PyArray_ISWRITEABLE(pyArray)) + return 0; + return EigenFromPy<MatType>::convertible(pyArray); + } + + static void registration() + { + bp::converter::registry::push_back + (reinterpret_cast<void *(*)(_object *)>(&EigenFromPy::convertible), + &eigen_from_py_construct<RefType>,bp::type_id<RefType>()); + } + }; + + template<typename MatType, int Options, typename Stride> + struct EigenFromPy<const Eigen::Ref<const MatType,Options,Stride> > + { + typedef const Eigen::Ref<const MatType,Options,Stride> ConstRefType; + typedef typename MatType::Scalar Scalar; + + /// \brief Determine if pyObj can be converted into a MatType object + static void* convertible(PyArrayObject * pyArray) + { + return EigenFromPy<MatType>::convertible(pyArray); + } + + static void registration() + { + bp::converter::registry::push_back + (reinterpret_cast<void *(*)(_object *)>(&EigenFromPy::convertible), + &eigen_from_py_construct<ConstRefType>,bp::type_id<ConstRefType>()); + } + }; + // Template specialization for Eigen::Ref template<typename MatType> struct EigenFromPyConverter< eigenpy::Ref<MatType> > diff --git a/include/eigenpy/fwd.hpp b/include/eigenpy/fwd.hpp index 3bc5937d05caebcd8be7c367693144acb31364b1..50a796e4ab634c070d09ab9b81e00571ec64416f 100644 --- a/include/eigenpy/fwd.hpp +++ b/include/eigenpy/fwd.hpp @@ -14,15 +14,13 @@ #include "eigenpy/numpy.hpp" #undef NO_IMPORT_ARRAY -#ifdef NPY_ALIGNED #if EIGEN_VERSION_AT_LEAST(3,2,90) #define EIGENPY_DEFAULT_ALIGNMENT_VALUE Eigen::Aligned16 #else #define EIGENPY_DEFAULT_ALIGNMENT_VALUE Eigen::Aligned #endif -#else - #define EIGENPY_DEFAULT_ALIGNMENT_VALUE Eigen::Unaligned -#endif + +#define EIGENPY_NO_ALIGNMENT_VALUE Eigen::Unaligned #include "eigenpy/expose.hpp" diff --git a/include/eigenpy/map.hpp b/include/eigenpy/map.hpp index 487506cd0116e01b23fa622a223e737c8281dac0..33bc323129223a96343046ba1805761aa1731dd0 100644 --- a/include/eigenpy/map.hpp +++ b/include/eigenpy/map.hpp @@ -12,18 +12,17 @@ namespace eigenpy { - template<typename MatType, typename InputScalar, bool IsVector> + template<typename MatType, typename InputScalar, int AlignmentValue, typename Stride, bool IsVector = MatType::IsVectorAtCompileTime> struct MapNumpyTraits {}; /* Wrap a numpy::array with an Eigen::Map. No memory copy. */ - template<typename MatType, typename InputScalar> + template<typename MatType, typename InputScalar, int AlignmentValue = EIGENPY_NO_ALIGNMENT_VALUE, typename Stride = typename StrideType<MatType>::type> struct MapNumpy { - typedef MapNumpyTraits<MatType, InputScalar, MatType::IsVectorAtCompileTime> Impl; + typedef MapNumpyTraits<MatType, InputScalar, AlignmentValue, Stride> Impl; typedef typename Impl::EigenMap EigenMap; - typedef typename Impl::Stride Stride; - static inline EigenMap map( PyArrayObject* pyArray ); + static EigenMap map(PyArrayObject* pyArray); }; } // namespace eigenpy @@ -34,19 +33,23 @@ namespace eigenpy namespace eigenpy { - template<typename MatType, typename InputScalar> - struct MapNumpyTraits<MatType,InputScalar,false> + template<typename MatType, typename InputScalar, int AlignmentValue, typename Stride> + struct MapNumpyTraits<MatType,InputScalar,AlignmentValue,Stride,false> { - typedef typename StrideType<MatType>::type Stride; - typedef Eigen::Matrix<InputScalar,MatType::RowsAtCompileTime,MatType::ColsAtCompileTime> EquivalentInputMatrixType; - typedef Eigen::Map<EquivalentInputMatrixType,EIGENPY_DEFAULT_ALIGNMENT_VALUE,Stride> EigenMap; + typedef Eigen::Matrix<InputScalar,MatType::RowsAtCompileTime,MatType::ColsAtCompileTime,MatType::Options> EquivalentInputMatrixType; + typedef Eigen::Map<EquivalentInputMatrixType,AlignmentValue,Stride> EigenMap; - static EigenMap mapImpl( PyArrayObject* pyArray ) + static EigenMap mapImpl(PyArrayObject* pyArray) { + enum { + OuterStrideAtCompileTime = Stride::OuterStrideAtCompileTime, + InnerStrideAtCompileTime = Stride::InnerStrideAtCompileTime, + }; + assert(PyArray_NDIM(pyArray) == 2 || PyArray_NDIM(pyArray) == 1); const long int itemsize = PyArray_ITEMSIZE(pyArray); - int stride1 = -1, stride2 = -1; + int inner_stride = -1, outer_stride = -1; int rows = -1, cols = -1; if(PyArray_NDIM(pyArray) == 2) { @@ -57,8 +60,17 @@ namespace eigenpy rows = (int)PyArray_DIMS(pyArray)[0]; cols = (int)PyArray_DIMS(pyArray)[1]; - stride1 = (int)PyArray_STRIDE(pyArray, 0) / (int)itemsize; - stride2 = (int)PyArray_STRIDE(pyArray, 1) / (int)itemsize; + + if(EquivalentInputMatrixType::IsRowMajor) + { + inner_stride = (int)PyArray_STRIDE(pyArray, 1) / (int)itemsize; + outer_stride = (int)PyArray_STRIDE(pyArray, 0) / (int)itemsize; + } + else + { + inner_stride = (int)PyArray_STRIDE(pyArray, 0) / (int)itemsize; + outer_stride = (int)PyArray_STRIDE(pyArray, 1) / (int)itemsize; + } } else if(PyArray_NDIM(pyArray) == 1) { @@ -68,33 +80,40 @@ namespace eigenpy rows = (int)PyArray_DIMS(pyArray)[0]; cols = 1; - stride1 = (int)PyArray_STRIDE(pyArray, 0) / (int)itemsize; - stride2 = 0; + inner_stride = (int)PyArray_STRIDE(pyArray, 0) / (int)itemsize; + outer_stride = 0; } - Stride stride(stride2,stride1); + // Specific care for Eigen::Stride<-1,0> + if(InnerStrideAtCompileTime==0 && OuterStrideAtCompileTime==Eigen::Dynamic) + { + outer_stride = std::max(inner_stride,outer_stride); inner_stride = 0; + } - if( (MatType::RowsAtCompileTime!=rows) - && (MatType::RowsAtCompileTime!=Eigen::Dynamic) ) + Stride stride(OuterStrideAtCompileTime==Eigen::Dynamic?outer_stride:OuterStrideAtCompileTime, + InnerStrideAtCompileTime==Eigen::Dynamic?inner_stride:InnerStrideAtCompileTime); + + if( (MatType::RowsAtCompileTime != rows) + && (MatType::RowsAtCompileTime != Eigen::Dynamic) ) { throw eigenpy::Exception("The number of rows does not fit with the matrix type."); } - if( (MatType::ColsAtCompileTime!=cols) - && (MatType::ColsAtCompileTime!=Eigen::Dynamic) ) + + if( (MatType::ColsAtCompileTime != cols) + && (MatType::ColsAtCompileTime != Eigen::Dynamic) ) { throw eigenpy::Exception("The number of columns does not fit with the matrix type."); } InputScalar* pyData = reinterpret_cast<InputScalar*>(PyArray_DATA(pyArray)); - return EigenMap( pyData, rows, cols, stride ); + return EigenMap(pyData, rows, cols, stride); } }; - template<typename MatType, typename InputScalar> - struct MapNumpyTraits<MatType,InputScalar,true> + template<typename MatType, typename InputScalar, int AlignmentValue, typename Stride> + struct MapNumpyTraits<MatType,InputScalar,AlignmentValue,Stride,true> { - typedef typename StrideType<MatType>::type Stride; - typedef Eigen::Matrix<InputScalar,MatType::RowsAtCompileTime,MatType::ColsAtCompileTime> EquivalentInputMatrixType; - typedef Eigen::Map<EquivalentInputMatrixType,EIGENPY_DEFAULT_ALIGNMENT_VALUE,Stride> EigenMap; + typedef Eigen::Matrix<InputScalar,MatType::RowsAtCompileTime,MatType::ColsAtCompileTime,MatType::Options> EquivalentInputMatrixType; + typedef Eigen::Map<EquivalentInputMatrixType,AlignmentValue,Stride> EigenMap; - static EigenMap mapImpl( PyArrayObject* pyArray ) + static EigenMap mapImpl(PyArrayObject* pyArray) { assert( PyArray_NDIM(pyArray) <= 2 ); @@ -110,8 +129,8 @@ namespace eigenpy const long int itemsize = PyArray_ITEMSIZE(pyArray); const int stride = (int) PyArray_STRIDE(pyArray, rowMajor) / (int) itemsize;; - if( (MatType::MaxSizeAtCompileTime!=R) - && (MatType::MaxSizeAtCompileTime!=Eigen::Dynamic) ) + if( (MatType::MaxSizeAtCompileTime != R) + && (MatType::MaxSizeAtCompileTime != Eigen::Dynamic) ) { throw eigenpy::Exception("The number of elements does not fit with the vector type."); } InputScalar* pyData = reinterpret_cast<InputScalar*>(PyArray_DATA(pyArray)); @@ -120,11 +139,11 @@ namespace eigenpy } }; - template<typename MatType, typename InputScalar> - typename MapNumpy<MatType,InputScalar>::EigenMap - MapNumpy<MatType,InputScalar>::map(PyArrayObject * pyArray) + template<typename MatType, typename InputScalar, int AlignmentValue, typename Stride> + typename MapNumpy<MatType,InputScalar,AlignmentValue,Stride>::EigenMap + MapNumpy<MatType,InputScalar,AlignmentValue,Stride>::map(PyArrayObject * pyArray) { - return Impl::mapImpl(pyArray); + return Impl::mapImpl(pyArray); } } // namespace eigenpy diff --git a/include/eigenpy/numpy-allocator.hpp b/include/eigenpy/numpy-allocator.hpp index 02a74215abe2dd198877dcb9c79b759eea33cff3..59885b37ebb5a3cc70f918e72e3aeb3f4088e360 100644 --- a/include/eigenpy/numpy-allocator.hpp +++ b/include/eigenpy/numpy-allocator.hpp @@ -50,6 +50,15 @@ namespace eigenpy } }; +#if EIGEN_VERSION_AT_LEAST(3,2,0) + + template<typename MatType, int Options, typename Stride> + struct NumpyAllocator<Eigen::Ref<MatType,Options,Stride> > : NumpyAllocator<MatType &> + { + }; + +#endif + template<typename MatType> struct NumpyAllocator<const MatType &> { @@ -69,6 +78,15 @@ namespace eigenpy return pyArray; } }; + +#if EIGEN_VERSION_AT_LEAST(3,2,0) + + template<typename MatType, int Options, typename Stride> + struct NumpyAllocator<const Eigen::Ref<const MatType,Options,Stride> > : NumpyAllocator<const MatType &> + { + }; + +#endif } #endif // ifndef __eigenpy_numpy_allocator_hpp__ diff --git a/include/eigenpy/ref.hpp b/include/eigenpy/ref.hpp index 4ffff43950e4e69fd0464267de8be8a4835c281c..c2eb21d5b6bbf0e7249183296327a121c829f945 100644 --- a/include/eigenpy/ref.hpp +++ b/include/eigenpy/ref.hpp @@ -20,10 +20,10 @@ namespace eigenpy template<typename PlainObjectTypeT> struct Ref - : Eigen::Ref<PlainObjectTypeT,EIGENPY_DEFAULT_ALIGNMENT_VALUE,typename StrideType<PlainObjectTypeT>::type> + : Eigen::Ref<PlainObjectTypeT,EIGENPY_NO_ALIGNMENT_VALUE,typename StrideType<PlainObjectTypeT>::type> { public: - typedef Eigen::Ref<PlainObjectTypeT,EIGENPY_DEFAULT_ALIGNMENT_VALUE,typename eigenpy::template StrideType<PlainObjectTypeT>::type> Base; + typedef Eigen::Ref<PlainObjectTypeT,EIGENPY_NO_ALIGNMENT_VALUE,typename eigenpy::template StrideType<PlainObjectTypeT>::type> Base; private: typedef Eigen::internal::traits<Base> Traits; diff --git a/include/eigenpy/stride.hpp b/include/eigenpy/stride.hpp index ab43349bf76647b8a14c1ecf1a9669f9b760eead..52f5656f1d36333aaafefcc2c971b0f70f04fe5d 100644 --- a/include/eigenpy/stride.hpp +++ b/include/eigenpy/stride.hpp @@ -10,17 +10,18 @@ namespace eigenpy { - template<typename MatType, bool IsVectorAtCompileTime = MatType::IsVectorAtCompileTime> + template<typename MatType, int InnerStride = Eigen::Dynamic, int OuterStride = Eigen::Dynamic, bool IsVectorAtCompileTime = MatType::IsVectorAtCompileTime> struct StrideType { - typedef Eigen::Stride<Eigen::Dynamic,Eigen::Dynamic> type; + typedef Eigen::Stride<OuterStride,InnerStride> type; }; - template<typename MatType> - struct StrideType<MatType,true> + template<typename MatType, int InnerStride, int OuterStride> + struct StrideType<MatType,InnerStride,OuterStride,true> { - typedef Eigen::InnerStride<Eigen::Dynamic> type; + typedef Eigen::InnerStride<InnerStride> type; }; + } #endif // ifndef __eigenpy_stride_hpp__ diff --git a/include/eigenpy/utils/is-aligned.hpp b/include/eigenpy/utils/is-aligned.hpp new file mode 100644 index 0000000000000000000000000000000000000000..e87691158b76f249754f7844826e3914aced7b06 --- /dev/null +++ b/include/eigenpy/utils/is-aligned.hpp @@ -0,0 +1,16 @@ +// +// Copyright (c) 2020 INRIA +// + +#ifndef __eigenpy_utils_is_aligned_hpp__ +#define __eigenpy_utils_is_aligned_hpp__ + +namespace eigenpy +{ + inline bool is_aligned(void * ptr, std::size_t alignment) + { + return (reinterpret_cast<std::size_t>(ptr) & (alignment - 1)) == 0; + } +} + +#endif diff --git a/src/numpy.cpp b/src/numpy.cpp index 4c76caef62ce2d8e7a14f5a2c7a06f14f081e4b0..61a91f33be39ce63481b51106637312804172773 100644 --- a/src/numpy.cpp +++ b/src/numpy.cpp @@ -13,6 +13,5 @@ namespace eigenpy 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 17bf479fe818ee27c9e0997c626b4399b797b4ef..1cb974d741f108bbee0267d401e272b17f67c2cd 100644 --- a/unittest/CMakeLists.txt +++ b/unittest/CMakeLists.txt @@ -34,13 +34,15 @@ 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") + ADD_LIB_UNIT_TEST(eigen_ref "eigen3") + ADD_LIB_UNIT_TEST(eigenpy_ref "eigen3") 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-eigen-ref" "unittest/python/test_eigen_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/eigen_ref.cpp b/unittest/eigen_ref.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1e58cfee4c7760169a605a79f3547b4a43369081 --- /dev/null +++ b/unittest/eigen_ref.cpp @@ -0,0 +1,59 @@ +/* + * Copyright 2014-2019, CNRS + * Copyright 2018-2020, INRIA + */ + +#include "eigenpy/eigenpy.hpp" +#include <iostream> + +using namespace Eigen; +using namespace eigenpy; + +template<typename MatType> +void printMatrix(const Eigen::Ref<const MatType> mat) +{ + if(MatType::IsVectorAtCompileTime) + std::cout << "isVector" << std::endl; + std::cout << "size: cols " << mat.cols() << " rows " << mat.rows() << std::endl; + std::cout << mat << std::endl; +} + +template<typename VecType> +void printVector(const Eigen::Ref<const VecType> & vec) +{ + EIGEN_STATIC_ASSERT_VECTOR_ONLY(VecType); + printMatrix(vec); +} + +template<typename MatType> +void setOnes(Eigen::Ref<MatType> mat) +{ + mat.setOnes(); +} + +template<typename MatType> +void fill(Eigen::Ref<MatType> mat, const typename MatType::Scalar & value) +{ + mat.fill(value); +} + +BOOST_PYTHON_MODULE(eigen_ref) +{ + namespace bp = boost::python; + eigenpy::enableEigenPy(); + + bp::def("printMatrix", printMatrix<Vector3d>); + bp::def("printMatrix", printMatrix<VectorXd>); + bp::def("printMatrix", printMatrix<MatrixXd>); + + bp::def("printVector", printVector<VectorXd>); + bp::def("printRowVector", printVector<RowVectorXd>); + + bp::def("setOnes", setOnes<Vector3d>); + bp::def("setOnes", setOnes<VectorXd>); + bp::def("setOnes", setOnes<MatrixXd>); + + bp::def("fillVec3", fill<Vector3d>); + bp::def("fillVec", fill<VectorXd>); + bp::def("fill", fill<MatrixXd>); +} diff --git a/unittest/ref.cpp b/unittest/eigenpy_ref.cpp similarity index 95% rename from unittest/ref.cpp rename to unittest/eigenpy_ref.cpp index 094ae8590f6690b8e8fc0865c44175111058ba23..165e0d96402d5432c486cadfc8b509fbe6d40937 100644 --- a/unittest/ref.cpp +++ b/unittest/eigenpy_ref.cpp @@ -1,6 +1,6 @@ /* * Copyright 2014-2019, CNRS - * Copyright 2018-2019, INRIA + * Copyright 2018-2020, INRIA */ #include "eigenpy/eigenpy.hpp" @@ -37,9 +37,7 @@ void setOnes_wrap(eigenpy::Ref<MatType> mat) setOnes(mat); } - - -BOOST_PYTHON_MODULE(ref) +BOOST_PYTHON_MODULE(eigenpy_ref) { namespace bp = boost::python; eigenpy::enableEigenPy(); diff --git a/unittest/python/test_eigen_ref.py b/unittest/python/test_eigen_ref.py new file mode 100644 index 0000000000000000000000000000000000000000..fe8cd29e386162e52b5292681d6611f228532524 --- /dev/null +++ b/unittest/python/test_eigen_ref.py @@ -0,0 +1,16 @@ +import numpy as np +from eigen_ref import * + +def test(mat): + + printMatrix(mat) + fill(mat,1.) + printMatrix(mat) + assert np.array_equal(mat,np.full(mat.shape,1.)) + +rows = 10 +cols = 30 + +mat = np.array(np.zeros((rows,cols))) + +test(mat)