diff --git a/CMakeLists.txt b/CMakeLists.txt index b4b12e3773a362803c843fcfad18289be96542b6..800c89aed78a9eb6716f25e719be417996a5a4ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -164,6 +164,7 @@ set(${PROJECT_NAME}_HEADERS include/eigenpy/std-vector.hpp include/eigenpy/pickle-vector.hpp include/eigenpy/stride.hpp + include/eigenpy/tensor/eigen-from-python.hpp include/eigenpy/swig.hpp include/eigenpy/version.hpp) diff --git a/include/eigenpy/details.hpp b/include/eigenpy/details.hpp index 12be4b09465632baf367c7cfb968f0ba56023540..a71c59f474a6482d6e6abc8d4434c03cd9abc7cb 100644 --- a/include/eigenpy/details.hpp +++ b/include/eigenpy/details.hpp @@ -17,24 +17,49 @@ #include "eigenpy/scalar-conversion.hpp" namespace eigenpy { -template <typename MatType, typename EigenEquivalentType> -EIGENPY_DEPRECATED void enableEigenPySpecific() { - enableEigenPySpecific<MatType>(); -} + +template <typename EigenType, + typename BaseType = typename get_eigen_base_type<EigenType>::type> +struct expose_eigen_type_impl; template <typename MatType> -void enableEigenPySpecific() { - if (check_registration<MatType>()) return; +struct expose_eigen_type_impl<MatType, Eigen::MatrixBase<MatType> > { + static void run() { + if (check_registration<MatType>()) return; - // to-python - EigenToPyConverter<MatType>::registration(); + // to-python + EigenToPyConverter<MatType>::registration(); #if EIGEN_VERSION_AT_LEAST(3, 2, 0) - EigenToPyConverter<Eigen::Ref<MatType> >::registration(); - EigenToPyConverter<const Eigen::Ref<const MatType> >::registration(); + EigenToPyConverter<Eigen::Ref<MatType> >::registration(); + EigenToPyConverter<const Eigen::Ref<const MatType> >::registration(); +#endif + + // from-python + EigenFromPyConverter<MatType>::registration(); + } +}; + +#ifdef EIGENPY_WITH_TENSOR_SUPPORT +template <typename TensorType> +struct expose_eigen_type_impl<TensorType, Eigen::TensorBase<TensorType> > { + static void run() { + if (check_registration<TensorType>()) return; + + // to-python + EigenToPyConverter<TensorType>::registration(); + EigenToPyConverter<Eigen::TensorRef<TensorType> >::registration(); + EigenToPyConverter< + const Eigen::TensorRef<const TensorType> >::registration(); + + // from-python + EigenFromPyConverter<TensorType>::registration(); + } +}; #endif - // from-python - EigenFromPyConverter<MatType>::registration(); +template <typename MatType> +void enableEigenPySpecific() { + expose_eigen_type_impl<MatType>::run(); } } // namespace eigenpy diff --git a/include/eigenpy/eigen-allocator.hpp b/include/eigenpy/eigen-allocator.hpp index 8f1a66949ebf0f3c3c5d590338bbca9c862f6eb2..1aa0b6a7bdad20b2dfa9a0f18ca1a8cd5691a83b 100644 --- a/include/eigenpy/eigen-allocator.hpp +++ b/include/eigenpy/eigen-allocator.hpp @@ -78,7 +78,9 @@ struct init_tensor { assert(PyArray_NDIM(pyArray) == Rank); typedef typename Tensor::Index Index; - Eigen::array<Index, Rank> dimensions = PyArray_DIMS(pyArray); + Eigen::array<Index, Rank> dimensions; + for (int k = 0; k < PyArray_NDIM(pyArray); ++k) + dimensions[k] = PyArray_DIMS(pyArray)[k]; if (storage) return new (storage) Tensor(dimensions); @@ -169,9 +171,13 @@ template <typename MatType> struct eigen_allocator_impl_matrix; template <typename MatType> -struct eigen_allocator_impl<MatType, Eigen::MatrixBase<MatType> > +struct eigen_allocator_impl<MatType, Eigen::MatrixBase<MatType>> : eigen_allocator_impl_matrix<MatType> {}; +template <typename MatType> +struct eigen_allocator_impl<const MatType, const Eigen::MatrixBase<MatType>> + : eigen_allocator_impl_matrix<const MatType> {}; + template <typename MatType> struct eigen_allocator_impl_matrix { typedef MatType Type; @@ -299,7 +305,19 @@ struct eigen_allocator_impl_matrix { #ifdef EIGENPY_WITH_TENSOR_SUPPORT template <typename TensorType> -struct eigen_allocator_impl<TensorType, Eigen::TensorBase<TensorType> > { +struct eigen_allocator_impl_tensor; + +template <typename TensorType> +struct eigen_allocator_impl<TensorType, Eigen::TensorBase<TensorType>> + : eigen_allocator_impl_tensor<TensorType> {}; + +template <typename TensorType> +struct eigen_allocator_impl<const TensorType, + const Eigen::TensorBase<TensorType>> + : eigen_allocator_impl_tensor<const TensorType> {}; + +template <typename TensorType> +struct eigen_allocator_impl_tensor { typedef typename TensorType::Scalar Scalar; static void allocate( PyArrayObject *pyArray, @@ -324,8 +342,11 @@ struct eigen_allocator_impl<TensorType, Eigen::TensorBase<TensorType> > { tensor) /// \brief Copy Python array into the input matrix mat. - template <typename MatrixDerived> - static void copy(PyArrayObject *pyArray, const TensorType &tensor) { + template <typename TensorDerived> + static void copy(PyArrayObject *pyArray, + const Eigen::TensorBase<TensorDerived> &tensor_) { + TensorDerived &tensor = const_cast<TensorDerived &>( + static_cast<const TensorDerived &>(tensor_)); const int pyArray_type_code = EIGENPY_GET_PY_ARRAY_TYPE(pyArray); const int Scalar_type_code = Register::getTypeCode<Scalar>(); @@ -450,7 +471,7 @@ inline bool is_arr_layout_compatible_with_mat_type(PyArrayObject *pyArray) { } template <typename MatType, int Options, typename Stride> -struct eigen_allocator_impl_matrix<Eigen::Ref<MatType, Options, Stride> > { +struct eigen_allocator_impl_matrix<Eigen::Ref<MatType, Options, Stride>> { typedef Eigen::Ref<MatType, Options, Stride> RefType; typedef typename MatType::Scalar Scalar; @@ -511,7 +532,7 @@ struct eigen_allocator_impl_matrix<Eigen::Ref<MatType, Options, Stride> > { template <typename MatType, int Options, typename Stride> struct eigen_allocator_impl_matrix< - const Eigen::Ref<const MatType, Options, Stride> > { + const Eigen::Ref<const MatType, Options, Stride>> { typedef const Eigen::Ref<const MatType, Options, Stride> RefType; typedef typename MatType::Scalar Scalar; @@ -572,6 +593,85 @@ struct eigen_allocator_impl_matrix< }; #endif +#ifdef EIGENPY_WITH_TENSOR_SUPPORT + +template <typename TensorType, typename TensorRef> +struct eigen_allocator_impl_tensor_ref; + +template <typename TensorType> +struct eigen_allocator_impl_tensor<Eigen::TensorRef<TensorType>> + : eigen_allocator_impl_tensor_ref<TensorType, + Eigen::TensorRef<TensorType>> {}; + +template <typename TensorType> +struct eigen_allocator_impl_tensor<const Eigen::TensorRef<const TensorType>> + : eigen_allocator_impl_tensor_ref< + const TensorType, const Eigen::TensorRef<const TensorType>> {}; + +template <typename TensorType, typename RefType> +struct eigen_allocator_impl_tensor_ref { + typedef typename TensorType::Scalar Scalar; + + typedef + typename ::boost::python::detail::referent_storage<RefType &>::StorageType + StorageType; + + static void allocate( + PyArrayObject *pyArray, + ::boost::python::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; + + static const int Options = Eigen::internal::traits<TensorType>::Options; + + bool need_to_allocate = false; + const int pyArray_type_code = EIGENPY_GET_PY_ARRAY_TYPE(pyArray); + const int Scalar_type_code = Register::getTypeCode<Scalar>(); + if (pyArray_type_code != Scalar_type_code) need_to_allocate |= true; + // bool incompatible_layout = + // !is_arr_layout_compatible_with_mat_type<MatType>(pyArray); + // need_to_allocate |= incompatible_layout; + // 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) { + TensorType *tensor_ptr; + tensor_ptr = details::init_tensor<TensorType>::run(pyArray); + RefType tensor_ref(*tensor_ptr); + + new (raw_ptr) StorageType(tensor_ref, pyArray, tensor_ptr); + + RefType &tensor = *reinterpret_cast<RefType *>(raw_ptr); + EigenAllocator<TensorType>::copy(pyArray, tensor); + } else { + assert(pyArray_type_code == Scalar_type_code); + typename NumpyMap<TensorType, Scalar, Options>::EigenMap numpyMap = + NumpyMap<TensorType, Scalar, Options>::map(pyArray); + RefType tensor_ref(numpyMap); + new (raw_ptr) StorageType(tensor_ref, pyArray); + } + } + + static void copy(RefType const &ref, PyArrayObject *pyArray) { + EigenAllocator<TensorType>::copy(ref, pyArray); + } +}; + +#endif + template <typename EigenType> struct EigenAllocator : eigen_allocator_impl<EigenType> {}; diff --git a/include/eigenpy/eigen-from-python.hpp b/include/eigenpy/eigen-from-python.hpp index 27f066c653f15b2e13144ac572ee55ea9e43d647..c8556e36328f90e71e066b433952a94b608028af 100644 --- a/include/eigenpy/eigen-from-python.hpp +++ b/include/eigenpy/eigen-from-python.hpp @@ -24,16 +24,6 @@ struct expected_pytype_for_arg<MatType, Eigen::MatrixBase<MatType> > { } }; -#ifdef EIGENPY_WITH_TENSOR_SUPPORT -template <typename TensorType> -struct expected_pytype_for_arg<TensorType, Eigen::TensorBase<TensorType> > { - static PyTypeObject const *get_pytype() { - PyTypeObject const *py_type = eigenpy::getPyArrayType(); - return py_type; - } -}; -#endif - } // namespace eigenpy namespace boost { @@ -47,13 +37,6 @@ struct expected_pytype_for_arg< : eigenpy::expected_pytype_for_arg< Eigen::Matrix<Scalar, Rows, Cols, Options, MaxRows, MaxCols> > {}; -#ifdef EIGENPY_WITH_TENSOR_SUPPORT -template <typename Scalar, int Rank, int Options, typename IndexType> -struct expected_pytype_for_arg<Eigen::Tensor<Scalar, Rank, Options, IndexType> > - : eigenpy::expected_pytype_for_arg< - Eigen::Tensor<Scalar, Rank, Options, IndexType> > {}; -#endif - } // namespace converter } // namespace python } // namespace boost @@ -75,44 +58,43 @@ struct copy_if_non_const<const MatType, true> { }; #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> +template <typename _RefType> struct referent_storage_eigen_ref { - typedef Eigen::Ref<MatType, Options, Stride> RefType; + typedef _RefType RefType; + typedef typename get_eigen_ref_plain_type<RefType>::type PlainObjectType; typedef typename ::eigenpy::aligned_storage< ::boost::python::detail::referent_size<RefType &>::value>::type AlignedStorage; referent_storage_eigen_ref() : pyArray(NULL), - mat_ptr(NULL), + plain_ptr(NULL), ref_ptr(reinterpret_cast<RefType *>(ref_storage.bytes)) {} referent_storage_eigen_ref(const RefType &ref, PyArrayObject *pyArray, - MatType *mat_ptr = NULL) + PlainObjectType *plain_ptr = NULL) : pyArray(pyArray), - mat_ptr(mat_ptr), + plain_ptr(plain_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); + if (plain_ptr != NULL && PyArray_ISWRITEABLE(pyArray)) + copy_if_non_const<PlainObjectType>::run(*plain_ptr, pyArray); Py_DECREF(pyArray); - if (mat_ptr != NULL) mat_ptr->~MatType(); + if (plain_ptr != NULL) plain_ptr->~PlainObjectType(); ref_ptr->~RefType(); } AlignedStorage ref_storage; PyArrayObject *pyArray; - MatType *mat_ptr; + PlainObjectType *plain_ptr; RefType *ref_ptr; }; #endif @@ -126,18 +108,16 @@ 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 Eigen::Ref<MatType, Options, Stride> RefType; + typedef ::eigenpy::details::referent_storage_eigen_ref<RefType> StorageType; typedef typename ::eigenpy::aligned_storage< referent_size<StorageType &>::value>::type 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 Eigen::Ref<const MatType, Options, Stride> RefType; + typedef ::eigenpy::details::referent_storage_eigen_ref<RefType> StorageType; typedef typename ::eigenpy::aligned_storage< referent_size<StorageType &>::value>::type type; }; @@ -189,7 +169,7 @@ struct rvalue_from_python_data<Eigen::PlainObjectBase<Derived> const &> 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; + typedef Eigen::Ref<MatType, Options, Stride> RefType; #if (!defined(__MWERKS__) || __MWERKS__ >= 0x3000) && \ (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 245) && \ @@ -197,7 +177,7 @@ struct rvalue_from_python_data<Eigen::Ref<MatType, Options, Stride> &> !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>, + BOOST_STATIC_ASSERT(BOOST_PYTHON_OFFSETOF(rvalue_from_python_storage<RefType>, stage1) == 0); #endif @@ -215,9 +195,7 @@ struct rvalue_from_python_data<Eigen::Ref<MatType, Options, Stride> &> // Destroys any object constructed in the storage. ~rvalue_from_python_data() { - typedef ::eigenpy::details::referent_storage_eigen_ref<MatType, Options, - Stride> - StorageType; + typedef ::eigenpy::details::referent_storage_eigen_ref<RefType> StorageType; if (this->stage1.convertible == this->storage.bytes) static_cast<StorageType *>((void *)this->storage.bytes)->~StorageType(); } @@ -228,7 +206,7 @@ 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; + typedef Eigen::Ref<const MatType, Options, Stride> RefType; #if (!defined(__MWERKS__) || __MWERKS__ >= 0x3000) && \ (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 245) && \ @@ -236,7 +214,7 @@ struct rvalue_from_python_data< !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>, + BOOST_STATIC_ASSERT(BOOST_PYTHON_OFFSETOF(rvalue_from_python_storage<RefType>, stage1) == 0); #endif @@ -254,9 +232,7 @@ struct rvalue_from_python_data< // Destroys any object constructed in the storage. ~rvalue_from_python_data() { - typedef ::eigenpy::details::referent_storage_eigen_ref<const MatType, - Options, Stride> - StorageType; + typedef ::eigenpy::details::referent_storage_eigen_ref<RefType> StorageType; if (this->stage1.convertible == this->storage.bytes) static_cast<StorageType *>((void *)this->storage.bytes)->~StorageType(); } @@ -577,4 +553,8 @@ struct EigenFromPy<const Eigen::Ref<const MatType, Options, Stride> > { } // namespace eigenpy +#ifdef EIGENPY_WITH_TENSOR_SUPPORT +#include "eigenpy/tensor/eigen-from-python.hpp" +#endif + #endif // __eigenpy_eigen_from_python_hpp__ diff --git a/include/eigenpy/eigen-to-python.hpp b/include/eigenpy/eigen-to-python.hpp index b6735aa0b36a88803c8ed3a3d2551b172946b99d..013f8cf71dfd9f4ec64073f08f8545fa852c7ef1 100644 --- a/include/eigenpy/eigen-to-python.hpp +++ b/include/eigenpy/eigen-to-python.hpp @@ -12,6 +12,7 @@ #include "eigenpy/eigen-allocator.hpp" #include "eigenpy/numpy-allocator.hpp" #include "eigenpy/numpy-type.hpp" +#include "eigenpy/registration.hpp" namespace boost { namespace python { @@ -62,8 +63,31 @@ namespace eigenpy { EIGENPY_DOCUMENTATION_START_IGNORE -template <typename MatType, typename Scalar> -struct eigen_to_py_impl { +template <typename EigenType, + typename BaseType = typename get_eigen_base_type<EigenType>::type> +struct eigen_to_py_impl; + +template <typename MatType> +struct eigen_to_py_impl_matrix; + +template <typename MatType> +struct eigen_to_py_impl<MatType, Eigen::MatrixBase<MatType> > + : eigen_to_py_impl_matrix<MatType> {}; + +template <typename MatType> +struct eigen_to_py_impl<MatType&, Eigen::MatrixBase<MatType> > + : eigen_to_py_impl_matrix<MatType&> {}; + +template <typename MatType> +struct eigen_to_py_impl<const MatType, const Eigen::MatrixBase<MatType> > + : eigen_to_py_impl_matrix<const MatType> {}; + +template <typename MatType> +struct eigen_to_py_impl<const MatType&, const Eigen::MatrixBase<MatType> > + : eigen_to_py_impl_matrix<const MatType&> {}; + +template <typename MatType> +struct eigen_to_py_impl_matrix { static PyObject* convert( typename boost::add_reference< typename boost::add_const<MatType>::type>::type mat) { @@ -95,40 +119,44 @@ struct eigen_to_py_impl { } }; -template <typename MatType, int Options, typename Stride, typename Scalar> -struct eigen_to_py_impl<Eigen::Ref<MatType, Options, Stride>, Scalar> { - static PyObject* convert(const Eigen::Ref<MatType, Options, Stride>& mat) { - typedef Eigen::Ref<MatType, Options, Stride> EigenRef; +#ifdef EIGENPY_WITH_TENSOR_SUPPORT +template <typename TensorType> +struct eigen_to_py_impl_tensor; - 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(); +template <typename TensorType> +struct eigen_to_py_impl<TensorType, Eigen::TensorBase<TensorType> > + : eigen_to_py_impl_tensor<TensorType> {}; - 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 = NumpyAllocator<EigenRef>::allocate(const_cast<EigenRef&>(mat), - 1, shape); - } else { - npy_intp shape[2] = {R, C}; - pyArray = NumpyAllocator<EigenRef>::allocate(const_cast<EigenRef&>(mat), - 2, shape); - } +template <typename TensorType> +struct eigen_to_py_impl<const TensorType, const Eigen::TensorBase<TensorType> > + : eigen_to_py_impl_tensor<const TensorType> {}; + +template <typename TensorType> +struct eigen_to_py_impl_tensor { + static PyObject* convert( + typename boost::add_reference< + typename boost::add_const<TensorType>::type>::type tensor) { + // typedef typename boost::remove_const< + // typename boost::remove_reference<Tensor>::type>::type + // TensorDerived; + + static const int NumIndices = TensorType::NumIndices; + npy_intp shape[NumIndices]; + for (int k = 0; k < NumIndices; ++k) shape[k] = tensor.dimension(k); + + PyArrayObject* pyArray = NumpyAllocator<TensorType>::allocate( + const_cast<TensorType&>(tensor), NumIndices, shape); // Create an instance (either np.array or np.matrix) return NumpyType::make(pyArray).ptr(); } }; +#endif EIGENPY_DOCUMENTATION_END_IGNORE -template <typename MatType, typename Scalar> -struct EigenToPy : eigen_to_py_impl<MatType, Scalar> { +template <typename EigenType, typename Scalar> +struct EigenToPy : eigen_to_py_impl<EigenType> { static PyTypeObject const* get_pytype() { return getPyArrayType(); } }; diff --git a/include/eigenpy/fwd.hpp b/include/eigenpy/fwd.hpp index 35ea8732de5d3fdcec9ee125d4f3a44c8c1bec05..4652110e73d964ff397130eae2b551b4c18bb52c 100644 --- a/include/eigenpy/fwd.hpp +++ b/include/eigenpy/fwd.hpp @@ -124,23 +124,49 @@ template <typename MatType, typename boost::remove_reference<MatType>::type::Scalar> struct EigenFromPy; +template <typename T> +struct remove_const_reference { + typedef typename boost::remove_const< + typename boost::remove_reference<T>::type>::type type; +}; + template <typename EigenType> struct get_eigen_base_type { - typedef typename boost::remove_const<EigenType>::type EigenType_; + typedef typename remove_const_reference<EigenType>::type EigenType_; typedef typename boost::mpl::if_< boost::is_base_of<Eigen::MatrixBase<EigenType_>, EigenType_>, - Eigen::MatrixBase<EigenType> + Eigen::MatrixBase<EigenType_> #ifdef EIGENPY_WITH_TENSOR_SUPPORT , typename boost::mpl::if_< boost::is_base_of<Eigen::TensorBase<EigenType_>, EigenType_>, - Eigen::TensorBase<EigenType>, void>::type + Eigen::TensorBase<EigenType_>, void>::type #else , void #endif - >::type type; + >::type _type; + + typedef typename boost::mpl::if_< + boost::is_const<typename boost::remove_reference<EigenType>::type>, + const _type, _type>::type type; +}; + +template <typename EigenType> +struct get_eigen_ref_plain_type; + +template <typename MatType, int Options, typename Stride> +struct get_eigen_ref_plain_type<Eigen::Ref<MatType, Options, Stride> > { + typedef typename Eigen::internal::traits< + Eigen::Ref<MatType, Options, Stride> >::PlainObjectType type; +}; + +#ifdef EIGENPY_WITH_TENSOR_SUPPORT +template <typename TensorType> +struct get_eigen_ref_plain_type<Eigen::TensorRef<TensorType> > { + typedef TensorType type; }; +#endif } // namespace eigenpy #include "eigenpy/alignment.hpp" diff --git a/include/eigenpy/numpy-allocator.hpp b/include/eigenpy/numpy-allocator.hpp index 0a522550a2c00f1d6c87510cc32aab51daeb20e8..3382eb32ab9d68a988f530c085c0907368351969 100644 --- a/include/eigenpy/numpy-allocator.hpp +++ b/include/eigenpy/numpy-allocator.hpp @@ -12,8 +12,38 @@ namespace eigenpy { +template <typename EigenType, typename BaseType> +struct numpy_allocator_impl; + +template <typename EigenType> +struct numpy_allocator_impl_matrix; + +template <typename MatType> +struct numpy_allocator_impl< + MatType, Eigen::MatrixBase<typename remove_const_reference<MatType>::type> > + : numpy_allocator_impl_matrix<MatType> {}; + template <typename MatType> -struct NumpyAllocator { +struct numpy_allocator_impl< + const MatType, + const Eigen::MatrixBase<typename remove_const_reference<MatType>::type> > + : numpy_allocator_impl_matrix<const MatType> {}; + +// template <typename MatType> +// struct numpy_allocator_impl<MatType &, Eigen::MatrixBase<MatType> > : +// numpy_allocator_impl_matrix<MatType &> +//{}; + +template <typename MatType> +struct numpy_allocator_impl<const MatType &, const Eigen::MatrixBase<MatType> > + : numpy_allocator_impl_matrix<const MatType &> {}; + +template <typename EigenType, + typename BaseType = typename get_eigen_base_type<EigenType>::type> +struct NumpyAllocator : numpy_allocator_impl<EigenType, BaseType> {}; + +template <typename MatType> +struct numpy_allocator_impl_matrix { template <typename SimilarMatrixType> static PyArrayObject *allocate( const Eigen::MatrixBase<SimilarMatrixType> &mat, npy_intp nd, @@ -29,27 +59,42 @@ struct NumpyAllocator { return pyArray; } +}; #ifdef EIGENPY_WITH_TENSOR_SUPPORT - template <typename Scalar, int Rank, int Options, typename IndexType> - static PyArrayObject *allocate( - const Eigen::Tensor<Scalar, Rank, Options, IndexType> &tensor, - npy_intp nd, npy_intp *shape) { - typedef Eigen::Tensor<Scalar, Rank, Options, IndexType> Tensor; - const int code = Register::getTypeCode<Scalar>(); + +template <typename TensorType> +struct numpy_allocator_impl_tensor; + +template <typename TensorType> +struct numpy_allocator_impl<TensorType, Eigen::TensorBase<TensorType> > + : numpy_allocator_impl_tensor<TensorType> {}; + +template <typename TensorType> +struct numpy_allocator_impl<const TensorType, + const Eigen::TensorBase<TensorType> > + : numpy_allocator_impl_tensor<const TensorType> {}; + +template <typename TensorType> +struct numpy_allocator_impl_tensor { + template <typename TensorDerived> + static PyArrayObject *allocate(const Eigen::TensorBase<TensorDerived> &tensor, + npy_intp nd, npy_intp *shape) { + const int code = Register::getTypeCode<typename TensorDerived::Scalar>(); PyArrayObject *pyArray = (PyArrayObject *)call_PyArray_SimpleNew( static_cast<int>(nd), shape, code); // Copy data - EigenAllocator<Tensor>::copy(tensor, pyArray); + EigenAllocator<TensorDerived>::copy( + static_cast<const TensorDerived &>(tensor), pyArray); return pyArray; } -#endif }; +#endif template <typename MatType> -struct NumpyAllocator<MatType &> { +struct numpy_allocator_impl_matrix<MatType &> { template <typename SimilarMatrixType> static PyArrayObject *allocate(Eigen::PlainObjectBase<SimilarMatrixType> &mat, npy_intp nd, npy_intp *shape) { @@ -75,7 +120,7 @@ struct NumpyAllocator<MatType &> { #if EIGEN_VERSION_AT_LEAST(3, 2, 0) template <typename MatType, int Options, typename Stride> -struct NumpyAllocator<Eigen::Ref<MatType, Options, Stride> > { +struct numpy_allocator_impl_matrix<Eigen::Ref<MatType, Options, Stride> > { typedef Eigen::Ref<MatType, Options, Stride> RefType; static PyArrayObject *allocate(RefType &mat, npy_intp nd, npy_intp *shape) { @@ -110,7 +155,7 @@ struct NumpyAllocator<Eigen::Ref<MatType, Options, Stride> > { #endif template <typename MatType> -struct NumpyAllocator<const MatType &> { +struct numpy_allocator_impl_matrix<const MatType &> { template <typename SimilarMatrixType> static PyArrayObject *allocate( const Eigen::PlainObjectBase<SimilarMatrixType> &mat, npy_intp nd, @@ -139,7 +184,8 @@ struct NumpyAllocator<const MatType &> { #if EIGEN_VERSION_AT_LEAST(3, 2, 0) template <typename MatType, int Options, typename Stride> -struct NumpyAllocator<const Eigen::Ref<const MatType, Options, Stride> > { +struct numpy_allocator_impl_matrix< + const Eigen::Ref<const MatType, Options, Stride> > { typedef const Eigen::Ref<const MatType, Options, Stride> RefType; static PyArrayObject *allocate(RefType &mat, npy_intp nd, npy_intp *shape) { @@ -173,6 +219,70 @@ struct NumpyAllocator<const Eigen::Ref<const MatType, Options, Stride> > { } }; +#endif + +#ifdef EIGENPY_WITH_TENSOR_SUPPORT +template <typename TensorType> +struct numpy_allocator_impl_tensor<Eigen::TensorRef<TensorType> > { + typedef Eigen::TensorRef<TensorType> RefType; + + static PyArrayObject *allocate(RefType &tensor, npy_intp nd, + npy_intp *shape) { + typedef typename RefType::Scalar Scalar; + static const bool IsRowMajor = TensorType::Options & Eigen::RowMajorBit; + enum { + NPY_ARRAY_MEMORY_CONTIGUOUS = + IsRowMajor ? NPY_ARRAY_CARRAY : NPY_ARRAY_FARRAY + }; + + if (NumpyType::sharedMemory()) { + const int Scalar_type_code = Register::getTypeCode<Scalar>(); + // static const Index NumIndices = TensorType::NumIndices; + + // const int elsize = + // call_PyArray_DescrFromType(Scalar_type_code)->elsize; npy_intp + // strides[NumIndices]; + + PyArrayObject *pyArray = (PyArrayObject *)call_PyArray_New( + getPyArrayType(), static_cast<int>(nd), shape, Scalar_type_code, NULL, + const_cast<Scalar *>(tensor.data()), + NPY_ARRAY_MEMORY_CONTIGUOUS | NPY_ARRAY_ALIGNED); + + return pyArray; + } else { + return NumpyAllocator<TensorType>::allocate(tensor, nd, shape); + } + } +}; + +template <typename TensorType> +struct numpy_allocator_impl_tensor<const Eigen::TensorRef<const TensorType> > { + typedef const Eigen::TensorRef<const TensorType> RefType; + + static PyArrayObject *allocate(RefType &tensor, npy_intp nd, + npy_intp *shape) { + typedef typename RefType::Scalar Scalar; + static const bool IsRowMajor = TensorType::Options & Eigen::RowMajorBit; + enum { + NPY_ARRAY_MEMORY_CONTIGUOUS_RO = + IsRowMajor ? NPY_ARRAY_CARRAY_RO : NPY_ARRAY_FARRAY_RO + }; + + if (NumpyType::sharedMemory()) { + const int Scalar_type_code = Register::getTypeCode<Scalar>(); + + PyArrayObject *pyArray = (PyArrayObject *)call_PyArray_New( + getPyArrayType(), static_cast<int>(nd), shape, Scalar_type_code, NULL, + const_cast<Scalar *>(tensor.data()), + NPY_ARRAY_MEMORY_CONTIGUOUS_RO | NPY_ARRAY_ALIGNED); + + return pyArray; + } else { + return NumpyAllocator<TensorType>::allocate(tensor, nd, shape); + } + } +}; + #endif } // namespace eigenpy diff --git a/include/eigenpy/numpy-map.hpp b/include/eigenpy/numpy-map.hpp index b881ee94821e473dc15bc72ddffbc2568afa52c4..f085a03705f85a5abd32305995c78b99f2ab2334 100644 --- a/include/eigenpy/numpy-map.hpp +++ b/include/eigenpy/numpy-map.hpp @@ -24,15 +24,15 @@ struct numpy_map_impl; template <typename MatType, typename InputScalar, int AlignmentValue, typename Stride> struct numpy_map_impl<MatType, InputScalar, AlignmentValue, Stride, - Eigen::MatrixBase<MatType> > + Eigen::MatrixBase<MatType>> : numpy_map_impl_matrix<MatType, InputScalar, AlignmentValue, Stride> {}; -#ifdef EIGENPY_WITH_TENSOR_SUPPORT -template <typename TensorType, typename InputScalar, int AlignmentValue, +template <typename MatType, typename InputScalar, int AlignmentValue, typename Stride> -struct numpy_map_impl_tensor; - -#endif +struct numpy_map_impl<const MatType, InputScalar, AlignmentValue, Stride, + const Eigen::MatrixBase<MatType>> + : numpy_map_impl_matrix<const MatType, InputScalar, AlignmentValue, + Stride> {}; template <typename MatType, typename InputScalar, int AlignmentValue, typename Stride> @@ -175,20 +175,39 @@ struct numpy_map_impl_matrix<MatType, InputScalar, AlignmentValue, Stride, }; #ifdef EIGENPY_WITH_TENSOR_SUPPORT + +template <typename TensorType, typename InputScalar, int AlignmentValue, + typename Stride> +struct numpy_map_impl_tensor; + template <typename TensorType, typename InputScalar, int AlignmentValue, typename Stride> struct numpy_map_impl<TensorType, InputScalar, AlignmentValue, Stride, - Eigen::TensorBase<TensorType> > { + Eigen::TensorBase<TensorType>> + : numpy_map_impl_tensor<TensorType, InputScalar, AlignmentValue, Stride> {}; + +template <typename TensorType, typename InputScalar, int AlignmentValue, + typename Stride> +struct numpy_map_impl<const TensorType, InputScalar, AlignmentValue, Stride, + const Eigen::TensorBase<TensorType>> + : numpy_map_impl_tensor<const TensorType, InputScalar, AlignmentValue, + Stride> {}; + +template <typename TensorType, typename InputScalar, int AlignmentValue, + typename Stride> +struct numpy_map_impl_tensor { typedef TensorType Tensor; typedef typename Eigen::internal::traits<TensorType>::Index Index; - static const Index NumIndices = TensorType::NumIndices; + static const int Options = Eigen::internal::traits<TensorType>::Options; + static const int NumIndices = TensorType::NumIndices; - typedef Eigen::Tensor<InputScalar, NumIndices, Tensor::Options, Index> + typedef Eigen::Tensor<InputScalar, NumIndices, Options, Index> EquivalentInputTensorType; typedef typename EquivalentInputTensorType::Dimensions Dimensions; - typedef Eigen::TensorMap<EquivalentInputTensorType, Tensor::Options> EigenMap; + typedef Eigen::TensorMap<EquivalentInputTensorType, Options> EigenMap; static EigenMap map(PyArrayObject* pyArray, bool swap_dimensions = false) { + EIGENPY_UNUSED_VARIABLE(swap_dimensions); assert(PyArray_NDIM(pyArray) == NumIndices || NumIndices == Eigen::Dynamic); Eigen::DSizes<Index, NumIndices> dimensions(PyArray_NDIM(pyArray)); diff --git a/include/eigenpy/stride.hpp b/include/eigenpy/stride.hpp index 0c10a242170913df308fd0d5bec04792e36435ec..41680986545d217f905956e628ac0a96e2fd7031 100644 --- a/include/eigenpy/stride.hpp +++ b/include/eigenpy/stride.hpp @@ -33,12 +33,25 @@ struct stride_type<MatrixType, InnerStride, OuterStride, type; }; +template <typename MatrixType, int InnerStride, int OuterStride> +struct stride_type<const MatrixType, InnerStride, OuterStride, + const Eigen::MatrixBase<MatrixType> > { + typedef typename stride_type_matrix<const MatrixType, InnerStride, + OuterStride>::type type; +}; + #ifdef EIGENPY_WITH_TENSOR_SUPPORT template <typename TensorType, int InnerStride, int OuterStride> struct stride_type<TensorType, InnerStride, OuterStride, Eigen::TensorBase<TensorType> > { typedef Eigen::Stride<OuterStride, InnerStride> type; }; + +template <typename TensorType, int InnerStride, int OuterStride> +struct stride_type<const TensorType, InnerStride, OuterStride, + const Eigen::TensorBase<TensorType> > { + typedef Eigen::Stride<OuterStride, InnerStride> type; +}; #endif template <typename EigenType, int InnerStride = Eigen::Dynamic, diff --git a/include/eigenpy/tensor/eigen-from-python.hpp b/include/eigenpy/tensor/eigen-from-python.hpp new file mode 100644 index 0000000000000000000000000000000000000000..b9bbdb6a93ef43bdee1705f7a31b48e79c22c19e --- /dev/null +++ b/include/eigenpy/tensor/eigen-from-python.hpp @@ -0,0 +1,217 @@ +// +// Copyright (c) 2023 INRIA +// + +#ifndef __eigenpy_tensor_eigen_from_python_hpp__ +#define __eigenpy_tensor_eigen_from_python_hpp__ + +#include "eigenpy/fwd.hpp" +#include "eigenpy/eigen-allocator.hpp" +#include "eigenpy/numpy-type.hpp" +#include "eigenpy/scalar-conversion.hpp" + +namespace eigenpy { + +template <typename TensorType> +struct expected_pytype_for_arg<TensorType, Eigen::TensorBase<TensorType> > { + static PyTypeObject const *get_pytype() { + PyTypeObject const *py_type = eigenpy::getPyArrayType(); + return py_type; + } +}; + +} // namespace eigenpy + +namespace boost { +namespace python { +namespace converter { + +template <typename Scalar, int Rank, int Options, typename IndexType> +struct expected_pytype_for_arg<Eigen::Tensor<Scalar, Rank, Options, IndexType> > + : eigenpy::expected_pytype_for_arg< + Eigen::Tensor<Scalar, Rank, Options, IndexType> > {}; + +template <typename Derived> +struct rvalue_from_python_data<Eigen::TensorBase<Derived> const &> + : ::eigenpy::rvalue_from_python_data<Derived const &> { + EIGENPY_RVALUE_FROM_PYTHON_DATA_INIT(Derived const &) +}; + +} // namespace converter +} // namespace python +} // namespace boost + +namespace boost { +namespace python { +namespace detail { +template <typename TensorType> +struct referent_storage<Eigen::TensorRef<TensorType> &> { + typedef Eigen::TensorRef<TensorType> RefType; + typedef ::eigenpy::details::referent_storage_eigen_ref<RefType> StorageType; + typedef typename ::eigenpy::aligned_storage< + referent_size<StorageType &>::value>::type type; +}; + +template <typename TensorType> +struct referent_storage<const Eigen::TensorRef<const TensorType> &> { + typedef Eigen::TensorRef<const TensorType> RefType; + typedef ::eigenpy::details::referent_storage_eigen_ref<RefType> StorageType; + typedef typename ::eigenpy::aligned_storage< + referent_size<StorageType &>::value>::type type; +}; +} // namespace detail +} // namespace python +} // namespace boost + +namespace eigenpy { + +template <typename TensorType> +struct eigen_from_py_impl<TensorType, Eigen::TensorBase<TensorType> > { + typedef typename TensorType::Scalar Scalar; + + /// \brief Determine if pyObj can be converted into a MatType object + static void *convertible(PyObject *pyObj); + + /// \brief Allocate memory and copy pyObj in the new storage + static void construct(PyObject *pyObj, + bp::converter::rvalue_from_python_stage1_data *memory); + + static void registration(); +}; + +template <typename TensorType> +void * +eigen_from_py_impl<TensorType, Eigen::TensorBase<TensorType> >::convertible( + PyObject *pyObj) { + if (!call_PyArray_Check(reinterpret_cast<PyObject *>(pyObj))) return 0; + + typedef typename Eigen::internal::traits<TensorType>::Index Index; + static const Index NumIndices = TensorType::NumIndices; + + PyArrayObject *pyArray = reinterpret_cast<PyArrayObject *>(pyObj); + + if (!np_type_is_convertible_into_scalar<Scalar>( + EIGENPY_GET_PY_ARRAY_TYPE(pyArray))) + return 0; + + if (!(PyArray_NDIM(pyArray) == NumIndices || NumIndices == 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; +} + +template <typename TensorType> +void eigen_from_py_impl<TensorType, Eigen::TensorBase<TensorType> >::construct( + PyObject *pyObj, bp::converter::rvalue_from_python_stage1_data *memory) { + eigen_from_py_construct<TensorType>(pyObj, memory); +} + +template <typename TensorType> +void eigen_from_py_impl<TensorType, + Eigen::TensorBase<TensorType> >::registration() { + bp::converter::registry::push_back( + reinterpret_cast<void *(*)(_object *)>(&eigen_from_py_impl::convertible), + &eigen_from_py_impl::construct, bp::type_id<TensorType>() +#ifndef BOOST_PYTHON_NO_PY_SIGNATURES + , + &eigenpy::expected_pytype_for_arg<TensorType>::get_pytype +#endif + ); +} + +template <typename TensorType> +struct eigen_from_py_converter_impl<TensorType, + Eigen::TensorBase<TensorType> > { + static void registration() { + EigenFromPy<TensorType>::registration(); + + // Add conversion to Eigen::TensorBase<TensorType> + typedef Eigen::TensorBase<TensorType> TensorBase; + EigenFromPy<TensorBase>::registration(); + + // Add conversion to Eigen::TensorRef<TensorType> + typedef Eigen::TensorRef<TensorType> RefType; + EigenFromPy<RefType>::registration(); + + // Add conversion to Eigen::TensorRef<const TensorType> + typedef const Eigen::TensorRef<const TensorType> ConstRefType; + EigenFromPy<ConstRefType>::registration(); + } +}; + +template <typename TensorType> +struct EigenFromPy<Eigen::TensorBase<TensorType> > : EigenFromPy<TensorType> { + typedef EigenFromPy<TensorType> EigenFromPyDerived; + typedef Eigen::TensorBase<TensorType> Base; + + static void registration() { + bp::converter::registry::push_back( + reinterpret_cast<void *(*)(_object *)>(&EigenFromPy::convertible), + &EigenFromPy::construct, bp::type_id<Base>() +#ifndef BOOST_PYTHON_NO_PY_SIGNATURES + , + &eigenpy::expected_pytype_for_arg<TensorType>::get_pytype +#endif + ); + } +}; + +template <typename TensorType> +struct EigenFromPy<Eigen::TensorRef<TensorType> > { + typedef Eigen::TensorRef<TensorType> RefType; + typedef typename TensorType::Scalar Scalar; + + /// \brief Determine if pyObj can be converted into a MatType object + static void *convertible(PyObject *pyObj) { + if (!call_PyArray_Check(pyObj)) return 0; + PyArrayObject *pyArray = reinterpret_cast<PyArrayObject *>(pyObj); + if (!PyArray_ISWRITEABLE(pyArray)) return 0; + return EigenFromPy<TensorType>::convertible(pyObj); + } + + static void registration() { + bp::converter::registry::push_back( + reinterpret_cast<void *(*)(_object *)>(&EigenFromPy::convertible), + &eigen_from_py_construct<RefType>, bp::type_id<RefType>() +#ifndef BOOST_PYTHON_NO_PY_SIGNATURES + , + &eigenpy::expected_pytype_for_arg<TensorType>::get_pytype +#endif + ); + } +}; + +template <typename TensorType> +struct EigenFromPy<const Eigen::TensorRef<const TensorType> > { + typedef const Eigen::TensorRef<const TensorType> ConstRefType; + typedef typename TensorType::Scalar Scalar; + + /// \brief Determine if pyObj can be converted into a MatType object + static void *convertible(PyObject *pyObj) { + return EigenFromPy<TensorType>::convertible(pyObj); + } + + static void registration() { + bp::converter::registry::push_back( + reinterpret_cast<void *(*)(_object *)>(&EigenFromPy::convertible), + &eigen_from_py_construct<ConstRefType>, bp::type_id<ConstRefType>() +#ifndef BOOST_PYTHON_NO_PY_SIGNATURES + , + &eigenpy::expected_pytype_for_arg<TensorType>::get_pytype +#endif + ); + } +}; + +} // namespace eigenpy + +#endif // __eigenpy_tensor_eigen_from_python_hpp__