diff --git a/CMakeLists.txt b/CMakeLists.txt index ee134df06..8ae903e99 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,12 +70,12 @@ ADD_REQUIRED_DEPENDENCY("eigen3 >= 3.0.5") SET(BOOST_COMPONENTS python) SEARCH_FOR_BOOST() # Add Boost path to include directories. -INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS}) +INCLUDE_DIRECTORIES(SYSTEM ${Boost_INCLUDE_DIRS}) FINDPYTHON(2.7 EXACT REQUIRED) -INCLUDE_DIRECTORIES(${PYTHON_INCLUDE_DIRS}) +INCLUDE_DIRECTORIES(SYSTEM ${PYTHON_INCLUDE_DIRS}) FIND_NUMPY() -INCLUDE_DIRECTORIES(${NUMPY_INCLUDE_DIRS}) +INCLUDE_DIRECTORIES(SYSTEM ${NUMPY_INCLUDE_DIRS}) # ---------------------------------------------------- # --- INCLUDE ---------------------------------------- @@ -103,6 +103,7 @@ SET(HEADERS registration.hpp angle-axis.hpp quaternion.hpp + ref.hpp ) MAKE_DIRECTORY("${${PROJECT_NAME}_BINARY_DIR}/include/eigenpy") diff --git a/src/details.hpp b/src/details.hpp index 949ed0c21..6fe8295d2 100644 --- a/src/details.hpp +++ b/src/details.hpp @@ -17,20 +17,13 @@ #ifndef __eigenpy_details_hpp__ #define __eigenpy_details_hpp__ -#include -#include - -#include -#ifdef NPY_1_8_API_VERSION -#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION -#endif +#include "eigenpy/fwd.hpp" #include #include #include "eigenpy/eigenpy.hpp" #include "eigenpy/registration.hpp" -#include "eigenpy/exception.hpp" #include "eigenpy/map.hpp" @@ -58,7 +51,7 @@ namespace eigenpy { return make((PyObject*)pyArray,copy); } bp::object make(PyObject* pyObj, bool copy = false) { - boost::python::object m + bp::object m = pyMatrixType(bp::object(bp::handle<>(pyObj)), bp::object(), copy); Py_INCREF(m.ptr()); return m; @@ -67,16 +60,50 @@ namespace eigenpy protected: PyMatrixType() { - pyModule = boost::python::import("numpy"); + pyModule = bp::import("numpy"); pyMatrixType = pyModule.attr("matrix"); } bp::object pyMatrixType; bp::object pyModule; }; + + template + struct EigenObjectAllocator + { + typedef MatType Type; + + static void allocate(PyArrayObject * pyArray, void * storage) + { + typename MapNumpy::EigenMap numpyMap = MapNumpy::map(pyArray); + new(storage) MatType(numpyMap); + } + + static void convert(Type const & mat , PyArrayObject * pyArray) + { + MapNumpy::map(pyArray) = mat; + } + }; + + template + struct EigenObjectAllocator< eigenpy::Ref > + { + typedef eigenpy::Ref Type; + + static void allocate(PyArrayObject * pyArray, void * storage) + { + typename MapNumpy::EigenMap numpyMap = MapNumpy::map(pyArray); + new(storage) Type(numpyMap); + } + + static void convert(Type const & mat , PyArrayObject * pyArray) + { + MapNumpy::map(pyArray) = mat; + } + }; /* --- TO PYTHON -------------------------------------------------------------- */ - template< typename MatType,typename EquivalentEigenType > + template struct EigenToPy { static PyObject* convert(MatType const& mat) @@ -88,139 +115,128 @@ namespace eigenpy npy_intp shape[2] = { R,C }; PyArrayObject* pyArray = (PyArrayObject*) - PyArray_SimpleNew(2, shape, NumpyEquivalentType::type_code); + PyArray_SimpleNew(2, shape, NumpyEquivalentType::type_code); - MapNumpy::map(pyArray) = mat; + EigenObjectAllocator::convert(mat,pyArray); return PyMatrixType::getInstance().make(pyArray).ptr(); } }; /* --- FROM PYTHON ------------------------------------------------------------ */ - namespace bp = boost::python; - - template - struct TraitsMatrixConstructor - { - static MatType & construct(void*storage,int /*r*/,int /*c*/) - { - return * new(storage) MatType(); - } - }; template - struct TraitsMatrixConstructor - { - static MatType & construct(void*storage,int r,int c) - { - return * new(storage) MatType(r,c); - } - }; - - template - struct TraitsMatrixConstructor - { - static MatType & construct(void*storage,int /*r*/,int c) - { - return * new(storage) MatType(R,c); - } - }; - - template - struct TraitsMatrixConstructor - { - static MatType & construct(void*storage,int r,int /*c*/) - { - return * new(storage) MatType(r,C); - } - }; - - - template struct EigenFromPy { EigenFromPy() { bp::converter::registry::push_back - (reinterpret_cast(&convertible), - &construct,bp::type_id()); + (reinterpret_cast(&convertible), + &construct,bp::type_id()); } - + // Determine if obj_ptr can be converted in a Eigenvec static void* convertible(PyArrayObject* obj_ptr) { - typedef typename MatType::Scalar T; - - if (!PyArray_Check(obj_ptr)) - { + if (!PyArray_Check(obj_ptr)) + { #ifndef NDEBUG - std::cerr << "The python object is not a numpy array." << std::endl; + std::cerr << "The python object is not a numpy array." << std::endl; #endif - return 0; - } - + return 0; + } + + if(MatType::IsVectorAtCompileTime) + { + if(PyArray_DIMS(obj_ptr)[0] > 1 && PyArray_DIMS(obj_ptr)[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(obj_ptr)[0] == 1) && (MatType::ColsAtCompileTime == 1)) + || ((PyArray_DIMS(obj_ptr)[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 (PyArray_NDIM(obj_ptr) != 2) - if ( (PyArray_NDIM(obj_ptr) !=1) || (! MatType::IsVectorAtCompileTime) ) - { + { + if ( (PyArray_NDIM(obj_ptr) !=1) || (! MatType::IsVectorAtCompileTime) ) + { #ifndef NDEBUG - std::cerr << "The number of dimension of the object is not correct." << std::endl; + std::cerr << "The number of dimension of the object is not correct." << std::endl; #endif - return 0; - } - - if ((PyArray_ObjectType(reinterpret_cast(obj_ptr), 0)) != NumpyEquivalentType::type_code) - { + return 0; + } + } + + if ((PyArray_ObjectType(reinterpret_cast(obj_ptr), 0)) + != NumpyEquivalentType::type_code) + { #ifndef NDEBUG - std::cerr << "The internal type as no Eigen equivalent." << std::endl; + std::cerr << "The internal type as no Eigen equivalent." << std::endl; #endif - return 0; - } + return 0; + } #ifdef NPY_1_8_API_VERSION if (!(PyArray_FLAGS(obj_ptr))) #else - if (!(PyArray_FLAGS(obj_ptr) & NPY_ALIGNED)) + if (!(PyArray_FLAGS(obj_ptr) & NPY_ALIGNED)) #endif - { + { #ifndef NDEBUG - std::cerr << "NPY non-aligned matrices are not implemented." << std::endl; + std::cerr << "NPY non-aligned matrices are not implemented." << std::endl; #endif - return 0; - } + return 0; + } return obj_ptr; } // Convert obj_ptr into a Eigenvec static void construct(PyObject* pyObj, - bp::converter::rvalue_from_python_stage1_data* memory) + bp::converter::rvalue_from_python_stage1_data* memory) { using namespace Eigen; - + PyArrayObject * pyArray = reinterpret_cast(pyObj); - typename MapNumpy::EigenMap numpyMap = MapNumpy::map(pyArray); - + assert((PyArray_DIMS(pyArray)[0]*) - ((void*)memory))->storage.bytes; - assert( (numpyMap.rows()::construct (storage,r,c); - memory->convertible = storage; + ((void*)memory))->storage.bytes; + + EigenObjectAllocator::allocate(pyArray,storage); - eigenMatrix = numpyMap; + memory->convertible = storage; } }; + #define numpy_import_array() {if (_import_array() < 0) {PyErr_Print(); PyErr_SetString(PyExc_ImportError, "numpy.core.multiarray failed to import"); } } template void enableEigenPySpecific() + { + enableEigenPySpecific(); + } + + template + void enableEigenPySpecific() { numpy_import_array(); if(check_registration()) return; - boost::python::to_python_converter >(); - EigenFromPy(); + bp::to_python_converter >(); + EigenFromPy(); } } // namespace eigenpy diff --git a/src/eigenpy.cpp b/src/eigenpy.cpp index d0b685eb1..dae15e9d2 100644 --- a/src/eigenpy.cpp +++ b/src/eigenpy.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 LAAS-CNRS + * Copyright (c) 2015-2018 LAAS-CNRS * * This file is part of eigenpy. * eigenpy is free software: you can redistribute it and/or @@ -22,17 +22,18 @@ namespace eigenpy /* Enable Eigen-Numpy serialization for a set of standard MatrixBase instance. */ void enableEigenPy() { + using namespace Eigen; Exception::registerException(); - enableEigenPySpecific(); - enableEigenPySpecific(); - enableEigenPySpecific(); - enableEigenPySpecific(); - - enableEigenPySpecific(); - enableEigenPySpecific(); - enableEigenPySpecific(); - enableEigenPySpecific(); + ENABLE_SPECIFIC_MATRIX_TYPE(MatrixXd); + ENABLE_SPECIFIC_MATRIX_TYPE(Matrix2d); + ENABLE_SPECIFIC_MATRIX_TYPE(Matrix3d); + ENABLE_SPECIFIC_MATRIX_TYPE(Matrix4d); + + ENABLE_SPECIFIC_MATRIX_TYPE(VectorXd); + ENABLE_SPECIFIC_MATRIX_TYPE(Vector2d); + ENABLE_SPECIFIC_MATRIX_TYPE(Vector3d); + ENABLE_SPECIFIC_MATRIX_TYPE(Vector4d); } } // namespace eigenpy diff --git a/src/eigenpy.hpp b/src/eigenpy.hpp index cd01d8bcc..9fcecc7ce 100644 --- a/src/eigenpy.hpp +++ b/src/eigenpy.hpp @@ -18,18 +18,27 @@ #define __eigenpy_eigenpy_hpp__ #include "eigenpy/fwd.hpp" -#include "eigenpy/memory.hpp" +#include "eigenpy/deprecated.hh" +#include "eigenpy/ref.hpp" + +#define ENABLE_SPECIFIC_MATRIX_TYPE(TYPE) \ + enableEigenPySpecific(); \ + enableEigenPySpecific< eigenpy::Ref >(); namespace eigenpy { /* Enable Eigen-Numpy serialization for a set of standard MatrixBase instance. */ void enableEigenPy(); + template + void enableEigenPySpecific(); + /* Enable the Eigen--Numpy serialization for the templated MatrixBase class. * The second template argument is used for inheritance of Eigen classes. If * using a native Eigen::MatrixBase, simply repeat the same arg twice. */ template - void enableEigenPySpecific(); + EIGENPY_DEPRECATED void enableEigenPySpecific(); + } // namespace eigenpy diff --git a/src/fwd.hpp b/src/fwd.hpp index ab9cf717f..d5d1f2e66 100644 --- a/src/fwd.hpp +++ b/src/fwd.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2014-2017, Nicolas Mansard and Justin Carpentier, LAAS-CNRS + * Copyright 2014-2018, Nicolas Mansard and Justin Carpentier, LAAS-CNRS * * This file is part of eigenpy. * eigenpy is free software: you can redistribute it and/or @@ -20,8 +20,17 @@ #include #include +#include #ifdef NPY_1_8_API_VERSION #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION #endif +#include + +#ifdef NPY_ALIGNED +#define EIGENPY_DEFAULT_ALIGNMENT_VALUE Eigen::Aligned16 +#else +#define EIGENPY_DEFAULT_ALIGNMENT_VALUE Eigen::Unaligned +#endif + #endif // ifndef __eigenpy_fwd_hpp__ diff --git a/src/map.hpp b/src/map.hpp index 0c0b633d4..2a2fb4b27 100644 --- a/src/map.hpp +++ b/src/map.hpp @@ -14,10 +14,9 @@ * with eigenpy. If not, see . */ -#include -#include +#include "eigenpy/fwd.hpp" #include -#include +#include "eigenpy/exception.hpp" namespace eigenpy { @@ -30,6 +29,7 @@ namespace eigenpy { typedef MapNumpyTraits Impl; typedef typename Impl::EigenMap EigenMap; + typedef typename Impl::Stride Stride; static inline EigenMap map( PyArrayObject* pyArray ); }; @@ -45,9 +45,9 @@ namespace eigenpy template struct MapNumpyTraits { - typedef Eigen::Stride Stride; - typedef Eigen::Map EigenMap; - typedef typename MatType::Scalar T; + typedef typename StrideType::type Stride; + typedef Eigen::Map EigenMap; + typedef typename MatType::Scalar Scalar; static EigenMap mapImpl( PyArrayObject* pyArray ) { @@ -63,6 +63,9 @@ namespace eigenpy const long int itemsize = PyArray_ITEMSIZE(pyArray); const int stride1 = (int)PyArray_STRIDE(pyArray, 0) / (int)itemsize; const int stride2 = (int)PyArray_STRIDE(pyArray, 1) / (int)itemsize; + Stride stride(stride2,stride1); + + if( (MatType::RowsAtCompileTime!=R) && (MatType::RowsAtCompileTime!=Eigen::Dynamic) ) @@ -71,17 +74,18 @@ namespace eigenpy && (MatType::ColsAtCompileTime!=Eigen::Dynamic) ) { throw eigenpy::Exception("The number of columns does not fit with the matrix type."); } - T* pyData = reinterpret_cast(PyArray_DATA(pyArray)); - return EigenMap( pyData, R,C, Stride(stride2,stride1) ); + Scalar* pyData = reinterpret_cast(PyArray_DATA(pyArray)); + + return EigenMap( pyData, R,C, stride ); } }; template struct MapNumpyTraits { - typedef Eigen::InnerStride Stride; - typedef Eigen::Map EigenMap; - typedef typename MatType::Scalar T; + typedef typename StrideType::type Stride; + typedef Eigen::Map EigenMap; + typedef typename MatType::Scalar Scalar; static EigenMap mapImpl( PyArrayObject* pyArray ) { @@ -101,8 +105,8 @@ namespace eigenpy && (MatType::MaxSizeAtCompileTime!=Eigen::Dynamic) ) { throw eigenpy::Exception("The number of elements does not fit with the vector type."); } - T* pyData = reinterpret_cast(PyArray_DATA(pyArray)); - return EigenMap( pyData, R, 1, Stride(stride) ); + Scalar* pyData = reinterpret_cast(PyArray_DATA(pyArray)); + return EigenMap( pyData, R, Stride(stride) ); } }; diff --git a/src/memory.hpp b/src/memory.hpp index 6a0b5f26f..e63d5f425 100644 --- a/src/memory.hpp +++ b/src/memory.hpp @@ -79,7 +79,7 @@ namespace boost { namespace python { namespace objects { \ \ Py_ssize_t holder_offset = reinterpret_cast(holder) \ - reinterpret_cast(&instance->storage) \ - + offsetof(instance_t, storage); \ + + static_cast(offsetof(instance_t, storage)); \ Py_SIZE(instance) = holder_offset; \ \ protect.cancel(); \ diff --git a/src/ref.hpp b/src/ref.hpp new file mode 100644 index 000000000..641228e18 --- /dev/null +++ b/src/ref.hpp @@ -0,0 +1,111 @@ +/* + * Copyright 2018, Justin Carpentier , LAAS-CNRS + * + * This file is part of eigenpy. + * eigenpy is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * eigenpy is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. You should + * have received a copy of the GNU Lesser General Public License along + * with eigenpy. If not, see . + */ + +#ifndef __eigenpy_ref_hpp__ +#define __eigenpy_ref_hpp__ + +#include "eigenpy/fwd.hpp" + +// For old Eigen versions, EIGEN_DEVICE_FUNC is not defined. +// We must define it just in the scope of this file. +#if not EIGEN_VERSION_AT_LEAST(3,2,91) +#define EIGEN_DEVICE_FUNC +#endif + +namespace eigenpy +{ + template + struct StrideType + { + typedef Eigen::Stride type; + }; + + template + struct StrideType + { + typedef Eigen::InnerStride type; + }; + + template + struct Ref : Eigen::Ref::type> + { + public: + typedef Eigen::Ref::type> Base; + + private: + typedef Eigen::internal::traits Traits; + template + EIGEN_DEVICE_FUNC inline Ref(const Eigen::PlainObjectBase& expr, + typename Eigen::internal::enable_if::MatchAtCompileTime),Derived>::type* = 0); + + public: + + typedef typename Eigen::internal::traits::Scalar Scalar; /*!< \brief Numeric type, e.g. float, double, int or std::complex. */ \ + typedef typename Eigen::NumTraits::Real RealScalar; /*!< \brief The underlying numeric type for composed scalar types. \details In cases where Scalar is e.g. std::complex, T were corresponding to RealScalar. */ \ + typedef typename Base::CoeffReturnType CoeffReturnType; /*!< \brief The return type for coefficient access. \details Depending on whether the object allows direct coefficient access (e.g. for a MatrixXd), this type is either 'const Scalar&' or simply 'Scalar' for objects that do not allow direct coefficient access. */ + typedef typename Eigen::internal::ref_selector::type Nested; + typedef typename Eigen::internal::traits::StorageKind StorageKind; +#if EIGEN_VERSION_AT_LEAST(3,2,91) + typedef typename Eigen::internal::traits::StorageIndex StorageIndex; +#else + typedef typename Eigen::internal::traits::Index StorageIndex; +#endif + enum { RowsAtCompileTime = Eigen::internal::traits::RowsAtCompileTime, + ColsAtCompileTime = Eigen::internal::traits::ColsAtCompileTime, + Flags = Eigen::internal::traits::Flags, + SizeAtCompileTime = Base::SizeAtCompileTime, + MaxSizeAtCompileTime = Base::MaxSizeAtCompileTime, + IsVectorAtCompileTime = Base::IsVectorAtCompileTime }; + using Base::derived; + using Base::const_cast_derived; + typedef typename Base::PacketScalar PacketScalar; + + template + EIGEN_DEVICE_FUNC inline Ref(Eigen::PlainObjectBase& expr, + typename Eigen::internal::enable_if::MatchAtCompileTime),Derived>::type* = 0) + : Base(expr.derived()) + {} + + template + EIGEN_DEVICE_FUNC inline Ref(const Eigen::DenseBase& expr, + typename Eigen::internal::enable_if::MatchAtCompileTime),Derived>::type* = 0) + : Base(expr.derived()) + {} + +#if EIGEN_COMP_MSVC_STRICT && (EIGEN_COMP_MSVC < 1900 || defined(__CUDACC_VER__)) // for older MSVC versions, as well as 1900 && CUDA 8, using the base operator is sufficient (cf Bugs 1000, 1324) + using Base::operator =; +#elif EIGEN_COMP_CLANG // workaround clang bug (see http://forum.kde.org/viewtopic.php?f=74&t=102653) + using Base::operator =; \ + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Ref& operator=(const Ref& other) { Base::operator=(other); return *this; } \ + template \ + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Ref& operator=(const Eigen::DenseBase& other) { Base::operator=(other.derived()); return *this; } +#else + using Base::operator =; \ + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Ref& operator=(const Ref& other) \ + { \ + Base::operator=(other); \ + return *this; \ + } +#endif + + }; // struct Ref +} + +#if not EIGEN_VERSION_AT_LEAST(3,2,91) +#undef EIGEN_DEVICE_FUNC +#endif + +#endif // ifndef __eigenpy_ref_hpp__ diff --git a/unittest/CMakeLists.txt b/unittest/CMakeLists.txt index 8524a1d62..8b0a15c8e 100644 --- a/unittest/CMakeLists.txt +++ b/unittest/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright (c) 2016 CNRS +# Copyright (c) 2016-2018 CNRS # # This file is part of eigenpy # eigenpy is free software: you can redistribute it @@ -40,4 +40,5 @@ ADD_CUSTOM_TARGET(check COMMAND ${CMAKE_CTEST_COMMAND}) ADD_LIB_UNIT_TEST(matrix "eigen3") ADD_LIB_UNIT_TEST(geometry "eigen3") +ADD_LIB_UNIT_TEST(ref "eigen3") diff --git a/unittest/ref.cpp b/unittest/ref.cpp new file mode 100644 index 000000000..0ca38fc05 --- /dev/null +++ b/unittest/ref.cpp @@ -0,0 +1,59 @@ +/* + * Copyright 2018, Justin Carpentier , LAAS-CNRS + * + * This file is part of eigenpy. + * eigenpy is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * eigenpy is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. You should + * have received a copy of the GNU Lesser General Public License along + * with eigenpy. If not, see . + */ + +#include "eigenpy/eigenpy.hpp" +#include + +using namespace Eigen; +using namespace eigenpy; + +template +void printMatrix(const eigenpy::Ref & 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 +void printVector(const eigenpy::Ref & mat) +{ + EIGEN_STATIC_ASSERT_VECTOR_ONLY(MatType); + printMatrix(mat); +} + +template +void setOnes(eigenpy::Ref mat) +{ + mat.setOnes(); +} + +BOOST_PYTHON_MODULE(ref) +{ + namespace bp = boost::python; + eigenpy::enableEigenPy(); + + bp::def("printMatrix", printMatrix); + bp::def("printMatrix", printMatrix); + bp::def("printMatrix", printMatrix); + + bp::def("printVector", printVector); + + bp::def("setOnes", setOnes); + bp::def("setOnes", setOnes); + bp::def("setOnes", setOnes); +}