diff --git a/include/eigenpy/details.hpp b/include/eigenpy/details.hpp index 58ed6ad064ebcd0e55ad55bae0dc989022d65d4d..dadfe458e2a60b91876547997ebe097e09ff2a1e 100644 --- a/include/eigenpy/details.hpp +++ b/include/eigenpy/details.hpp @@ -49,6 +49,20 @@ namespace boost { namespace python { namespace detail { std::size_t, value = sizeof(MatType)); }; + template<class MatType> + struct referent_size<Eigen::PlainObjectBase<MatType>&> + { + BOOST_STATIC_CONSTANT( + std::size_t, value = sizeof(MatType)); + }; + + template<class MatType> + struct referent_size<Eigen::PlainObjectBase<MatType> > + { + BOOST_STATIC_CONSTANT( + std::size_t, value = sizeof(MatType)); + }; + }}} namespace eigenpy @@ -65,7 +79,13 @@ namespace eigenpy { if(check_registration<MatType>()) return; + // to-python EigenToPyConverter<MatType>::registration(); +#if EIGEN_VERSION_AT_LEAST(3,2,0) + EigenToPyConverter< Eigen::Ref<MatType> >::registration(); +#endif + + // from-python EigenFromPyConverter<MatType>::registration(); } diff --git a/include/eigenpy/eigen-from-python.hpp b/include/eigenpy/eigen-from-python.hpp index 83beda216ff335ae42e7ab291d725d8dbf578c9c..1a55bcbaba45bdba843a86a979c41bf5f046fb3c 100644 --- a/include/eigenpy/eigen-from-python.hpp +++ b/include/eigenpy/eigen-from-python.hpp @@ -173,6 +173,14 @@ namespace boost { namespace python { namespace converter { EIGENPY_RVALUE_FROM_PYTHON_DATA_INIT(Derived const &) }; + /// \brief Template specialization of rvalue_from_python_data + template<typename Derived> + struct rvalue_from_python_data<Eigen::PlainObjectBase<Derived> const &> + : rvalue_from_python_data_eigen<Derived const &> + { + EIGENPY_RVALUE_FROM_PYTHON_DATA_INIT(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> &> @@ -425,6 +433,10 @@ namespace eigenpy typedef Eigen::EigenBase<MatType> EigenBase; EigenFromPy<EigenBase>::registration(); + // Add conversion to Eigen::PlainObjectBase<MatType> + typedef Eigen::PlainObjectBase<MatType> PlainObjectBase; + EigenFromPy<PlainObjectBase>::registration(); + #if EIGEN_VERSION_AT_LEAST(3,2,0) // Add conversion to Eigen::Ref<MatType> typedef Eigen::Ref<MatType> RefType; @@ -464,6 +476,20 @@ namespace eigenpy &EigenFromPy::construct,bp::type_id<Base>()); } }; + + template<typename MatType> + struct EigenFromPy< Eigen::PlainObjectBase<MatType> > : EigenFromPy<MatType> + { + typedef EigenFromPy<MatType> EigenFromPyDerived; + typedef Eigen::PlainObjectBase<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) diff --git a/include/eigenpy/eigen-to-python.hpp b/include/eigenpy/eigen-to-python.hpp index fac253712cbee8f3ee968c6080a5240c04b9bacb..bd89fec522f79a56932f8e0fd33732284f6bb6aa 100644 --- a/include/eigenpy/eigen-to-python.hpp +++ b/include/eigenpy/eigen-to-python.hpp @@ -82,6 +82,36 @@ namespace eigenpy } }; + template<typename MatType, int Options, typename Stride> + struct EigenToPy< Eigen::Ref<MatType,Options,Stride> > + { + static PyObject* convert(const Eigen::Ref<MatType,Options,Stride> & mat) + { + typedef Eigen::Ref<MatType,Options,Stride> EigenRef; + + 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 = 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); + } + + // Create an instance (either np.array or np.matrix) + return NumpyType::make(pyArray).ptr(); + } + }; + template<typename MatType> struct EigenToPyConverter { diff --git a/include/eigenpy/numpy-allocator.hpp b/include/eigenpy/numpy-allocator.hpp index a627c8abbd66dc810d92109a30817552c6675008..9cbeb8a0415c529564bbb2452966f88b947abe32 100644 --- a/include/eigenpy/numpy-allocator.hpp +++ b/include/eigenpy/numpy-allocator.hpp @@ -57,7 +57,7 @@ namespace eigenpy } else { - return NumpyAllocator<MatType>::allocate(mat.derived(),nd,shape); + return NumpyAllocator<MatType>::allocate(mat,nd,shape); } } }; @@ -65,8 +65,33 @@ 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 &> + struct NumpyAllocator<Eigen::Ref<MatType,Options,Stride> > { + typedef Eigen::Ref<MatType,Options,Stride> RefType; + + static PyArrayObject * allocate(RefType & mat, + npy_intp nd, npy_intp * shape) + { + typedef typename RefType::Scalar Scalar; + enum { NPY_ARRAY_MEMORY_CONTIGUOUS = RefType::IsRowMajor ? NPY_ARRAY_CARRAY : NPY_ARRAY_FARRAY }; + + 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, + mat.data(), + NPY_ARRAY_MEMORY_CONTIGUOUS | NPY_ARRAY_ALIGNED); + + return pyArray; + } + else + { + return NumpyAllocator<MatType>::allocate(mat,nd,shape); + } + } }; #endif @@ -88,14 +113,14 @@ namespace eigenpy static_cast<int>(nd), shape, Scalar_type_code, - const_cast<SimilarMatrixType &>(mat.derived()).data(), + const_cast<Scalar *>(mat.data()), NPY_ARRAY_MEMORY_CONTIGUOUS_RO | NPY_ARRAY_ALIGNED); return pyArray; } else { - return NumpyAllocator<MatType>::allocate(mat.derived(),nd,shape); + return NumpyAllocator<MatType>::allocate(mat,nd,shape); } } }; @@ -103,8 +128,34 @@ namespace eigenpy #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 &> + struct NumpyAllocator<const Eigen::Ref<const MatType,Options,Stride> > { + typedef const Eigen::Ref<const MatType,Options,Stride> RefType; + + template<typename SimilarMatrixType> + static PyArrayObject * allocate(RefType & 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 }; + + 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, + const_cast<Scalar *>(mat.data()), + NPY_ARRAY_MEMORY_CONTIGUOUS_RO | NPY_ARRAY_ALIGNED); + + return pyArray; + } + else + { + return NumpyAllocator<MatType>::allocate(mat,nd,shape); + } + } }; #endif diff --git a/unittest/eigen_ref.cpp b/unittest/eigen_ref.cpp index 1e58cfee4c7760169a605a79f3547b4a43369081..267f4a00666aed7082c38b1ebf334efae66f2113 100644 --- a/unittest/eigen_ref.cpp +++ b/unittest/eigen_ref.cpp @@ -37,6 +37,14 @@ void fill(Eigen::Ref<MatType> mat, const typename MatType::Scalar & value) mat.fill(value); } +template<typename MatType> +Eigen::Ref<MatType> asRef(const int rows, const int cols) +{ + static MatType mat(rows,cols); + std::cout << "mat:\n" << mat << std::endl; + return mat; +} + BOOST_PYTHON_MODULE(eigen_ref) { namespace bp = boost::python; @@ -56,4 +64,6 @@ BOOST_PYTHON_MODULE(eigen_ref) bp::def("fillVec3", fill<Vector3d>); bp::def("fillVec", fill<VectorXd>); bp::def("fill", fill<MatrixXd>); + + bp::def("asRef", asRef<MatrixXd>); } diff --git a/unittest/matrix.cpp b/unittest/matrix.cpp index ce8a1c191aafb95e000b85e4eb3d111d04f70fd1..fd4ebae04b87b10d864e27bd0a3cc5e86bdb2763 100644 --- a/unittest/matrix.cpp +++ b/unittest/matrix.cpp @@ -81,6 +81,12 @@ MatrixDerived base(const Eigen::MatrixBase<MatrixDerived> & m) return m.derived(); } +template<typename MatrixDerived> +MatrixDerived plain(const Eigen::PlainObjectBase<MatrixDerived> & m) +{ + return m.derived(); +} + template<typename Scalar> Eigen::Matrix<Scalar,6,6> matrix6(const Scalar & value) { @@ -123,6 +129,9 @@ BOOST_PYTHON_MODULE(matrix) bp::def("base", base<VectorXd>); bp::def("base", base<MatrixXd>); + + bp::def("plain", plain<VectorXd>); + bp::def("plain", plain<MatrixXd>); bp::def("matrix6", matrix6<double>); } diff --git a/unittest/python/test_eigen_ref.py b/unittest/python/test_eigen_ref.py index fe8cd29e386162e52b5292681d6611f228532524..76721a3f2bb6080b0e7f3a9df1a0f8965f3a33d1 100644 --- a/unittest/python/test_eigen_ref.py +++ b/unittest/python/test_eigen_ref.py @@ -8,6 +8,16 @@ def test(mat): printMatrix(mat) assert np.array_equal(mat,np.full(mat.shape,1.)) + A_ref = asRef(mat.shape[0],mat.shape[1]) + A_ref.fill(1.) + A_ref2 = asRef(mat.shape[0],mat.shape[1]) + + assert np.array_equal(A_ref,A_ref2) + + A_ref2.fill(0) + assert np.array_equal(A_ref,A_ref2) + + rows = 10 cols = 30 diff --git a/unittest/python/test_matrix.py b/unittest/python/test_matrix.py index 5734eb92af3f25b6569cff7cb50a3f37f8e676c9..1b1ea6e739147df959056af916bee23827f11da3 100644 --- a/unittest/python/test_matrix.py +++ b/unittest/python/test_matrix.py @@ -36,6 +36,10 @@ Mref = np.reshape(np.array(range(64),np.double),[8,8]) Mref_from_base = eigenpy.base(Mref) assert( np.array_equal(Mref,Mref_from_base) ); +# Test plain function +Mref_from_plain = eigenpy.plain(Mref) +assert( np.array_equal(Mref,Mref_from_plain) ); + if verbose: print("===> Matrix 8x8") M = Mref assert( np.array_equal(M,eigenpy.reflex(M,verbose)) );