Skip to content
Snippets Groups Projects
Unverified Commit 423191d6 authored by Justin Carpentier's avatar Justin Carpentier Committed by GitHub
Browse files

Merge pull request #79 from jcarpent/devel

Improve efficiency + fix bug in Eigen::MatrixBase
parents 48ff1fd1 02cdbf0b
No related branches found
No related tags found
No related merge requests found
......@@ -103,6 +103,7 @@ SET(${PROJECT_NAME}_HEADERS
include/eigenpy/quaternion.hpp
include/eigenpy/stride.hpp
include/eigenpy/ref.hpp
include/eigenpy/details/rvalue_from_python_data.hpp
)
INCLUDE_DIRECTORIES(${${PROJECT_NAME}_BINARY_DIR}/include)
......
Subproject commit ab4c798680d0a3ba4e84d3b7f25cc42d336eca1b
Subproject commit 242dc450ae43ca2182987c9588c21e8aab32d61c
......@@ -6,6 +6,7 @@
#ifndef __eigenpy_details_hpp__
#define __eigenpy_details_hpp__
#include "eigenpy/details/rvalue_from_python_data.hpp"
#include "eigenpy/fwd.hpp"
#include <patchlevel.h> // For PY_MAJOR_VERSION
......@@ -18,7 +19,6 @@
#define GET_PY_ARRAY_TYPE(array) PyArray_ObjectType(reinterpret_cast<PyObject *>(array), 0)
namespace eigenpy
{
template <typename SCALAR> struct NumpyEquivalentType {};
......@@ -146,33 +146,75 @@ namespace eigenpy
const int rows = (int)PyArray_DIMS(pyArray)[0];
const int cols = (int)PyArray_DIMS(pyArray)[1];
Type * mat_ptr = new(storage) Type(rows,cols);
Type * mat_ptr = new (storage) Type(rows,cols);
if(NumpyEquivalentType<Scalar>::type_code == GET_PY_ARRAY_TYPE(pyArray))
{
*mat_ptr = MapNumpy<MatType,Scalar>::map(pyArray); // avoid useless cast
return;
}
if(GET_PY_ARRAY_TYPE(pyArray) == NPY_INT)
{
*mat_ptr = MapNumpy<MatType,int>::map(pyArray).template cast<Scalar>();
return;
}
if(GET_PY_ARRAY_TYPE(pyArray) == NPY_LONG)
{
*mat_ptr = MapNumpy<MatType,long>::map(pyArray).template cast<Scalar>();
return;
}
if(GET_PY_ARRAY_TYPE(pyArray) == NPY_FLOAT)
{
*mat_ptr = MapNumpy<MatType,float>::map(pyArray).template cast<Scalar>();
return;
}
if(GET_PY_ARRAY_TYPE(pyArray) == NPY_DOUBLE)
{
*mat_ptr = MapNumpy<MatType,double>::map(pyArray).template cast<Scalar>();
return;
}
}
static void convert(Type const & mat , PyArrayObject * pyArray)
/// \brief Copy mat into the Python array using Eigen::Map
template<typename MatrixDerived>
static void convert(const Eigen::MatrixBase<MatrixDerived> & mat_,
PyArrayObject * pyArray)
{
const MatrixDerived & mat = const_cast<const MatrixDerived &>(mat_.derived());
if(NumpyEquivalentType<Scalar>::type_code == GET_PY_ARRAY_TYPE(pyArray))
{
MapNumpy<MatType,Scalar>::map(pyArray) = mat; // no cast needed
return;
}
if(GET_PY_ARRAY_TYPE(pyArray) == NPY_INT)
{
MapNumpy<MatType,int>::map(pyArray) = mat.template cast<int>();
return;
}
if(GET_PY_ARRAY_TYPE(pyArray) == NPY_LONG)
{
MapNumpy<MatType,long>::map(pyArray) = mat.template cast<long>();
return;
}
if(GET_PY_ARRAY_TYPE(pyArray) == NPY_FLOAT)
{
MapNumpy<MatType,float>::map(pyArray) = mat.template cast<float>();
return;
}
if(GET_PY_ARRAY_TYPE(pyArray) == NPY_DOUBLE)
{
MapNumpy<MatType,double>::map(pyArray) = mat.template cast<double>();
return;
}
}
};
......@@ -186,51 +228,47 @@ namespace eigenpy
static void allocate(PyArrayObject * pyArray, void * storage)
{
typename MapNumpy<MatType,Scalar>::EigenMap numpyMap = MapNumpy<MatType,Scalar>::map(pyArray);
new(storage) Type(numpyMap);
new (storage) Type(numpyMap);
}
static void convert(Type const & mat , PyArrayObject * pyArray)
static void convert(Type const & mat, PyArrayObject * pyArray)
{
if(GET_PY_ARRAY_TYPE(pyArray) == NPY_INT)
MapNumpy<MatType,int>::map(pyArray) = mat.template cast<int>();
if(GET_PY_ARRAY_TYPE(pyArray) == NPY_LONG)
MapNumpy<MatType,long>::map(pyArray) = mat.template cast<long>();
if(GET_PY_ARRAY_TYPE(pyArray) == NPY_FLOAT)
MapNumpy<MatType,float>::map(pyArray) = mat.template cast<float>();
if(GET_PY_ARRAY_TYPE(pyArray) == NPY_DOUBLE)
MapNumpy<MatType,double>::map(pyArray) = mat.template cast<double>();
EigenObjectAllocator<MatType>::convert(mat,pyArray);
}
};
#endif
/* --- TO PYTHON -------------------------------------------------------------- */
template<typename MatType>
struct EigenToPy
{
static PyObject* convert(MatType const& mat)
static PyObject* convert(MatType const & mat)
{
typedef typename MatType::Scalar T;
typedef typename MatType::Scalar Scalar;
assert( (mat.rows()<INT_MAX) && (mat.cols()<INT_MAX)
&& "Matrix range larger than int ... should never happen." );
const int R = (int)mat.rows(), C = (int)mat.cols();
PyArrayObject* pyArray;
if(C == 1 && NumpyType::getType() == ARRAY_TYPE)
// Allocate Python memory
if(C == 1 && NumpyType::getType() == ARRAY_TYPE) // Handle array with a single dimension
{
npy_intp shape[1] = { R };
pyArray = (PyArrayObject*) PyArray_SimpleNew(1, shape,
NumpyEquivalentType<T>::type_code);
NumpyEquivalentType<Scalar>::type_code);
}
else
{
npy_intp shape[2] = { R,C };
pyArray = (PyArrayObject*) PyArray_SimpleNew(2, shape,
NumpyEquivalentType<T>::type_code);
NumpyEquivalentType<Scalar>::type_code);
}
// Allocate memory
EigenObjectAllocator<MatType>::convert(mat,pyArray);
// Create an instance (either np.array or np.matrix)
return NumpyType::getInstance().make(pyArray).ptr();
}
};
......@@ -240,19 +278,19 @@ namespace eigenpy
template<typename MatType>
struct EigenFromPy
{
// Determine if obj_ptr can be converted in a Eigenvec
static void* convertible(PyArrayObject* obj_ptr)
/// \brief Determine if pyObj can be converted into a MatType object
static void* convertible(PyArrayObject* pyObj)
{
if (!PyArray_Check(obj_ptr))
if (!PyArray_Check(pyObj))
return 0;
if(MatType::IsVectorAtCompileTime)
{
// Special care of scalar matrix of dimension 1x1.
if(PyArray_DIMS(obj_ptr)[0] == 1 && PyArray_DIMS(obj_ptr)[1] == 1)
return obj_ptr;
if(PyArray_DIMS(pyObj)[0] == 1 && PyArray_DIMS(pyObj)[1] == 1)
return pyObj;
if(PyArray_DIMS(obj_ptr)[0] > 1 && PyArray_DIMS(obj_ptr)[1] > 1)
if(PyArray_DIMS(pyObj)[0] > 1 && PyArray_DIMS(pyObj)[1] > 1)
{
#ifndef NDEBUG
std::cerr << "The number of dimension of the object does not correspond to a vector" << std::endl;
......@@ -260,8 +298,8 @@ namespace eigenpy
return 0;
}
if(((PyArray_DIMS(obj_ptr)[0] == 1) && (MatType::ColsAtCompileTime == 1))
|| ((PyArray_DIMS(obj_ptr)[1] == 1) && (MatType::RowsAtCompileTime == 1)))
if(((PyArray_DIMS(pyObj)[0] == 1) && (MatType::ColsAtCompileTime == 1))
|| ((PyArray_DIMS(pyObj)[1] == 1) && (MatType::RowsAtCompileTime == 1)))
{
#ifndef NDEBUG
if(MatType::ColsAtCompileTime == 1)
......@@ -273,9 +311,9 @@ namespace eigenpy
}
}
if (PyArray_NDIM(obj_ptr) != 2)
if (PyArray_NDIM(pyObj) != 2)
{
if ( (PyArray_NDIM(obj_ptr) !=1) || (! MatType::IsVectorAtCompileTime) )
if ( (PyArray_NDIM(pyObj) !=1) || (! MatType::IsVectorAtCompileTime) )
{
#ifndef NDEBUG
std::cerr << "The number of dimension of the object is not correct." << std::endl;
......@@ -284,10 +322,10 @@ namespace eigenpy
}
}
if (PyArray_NDIM(obj_ptr) == 2)
if (PyArray_NDIM(pyObj) == 2)
{
const int R = (int)PyArray_DIMS(obj_ptr)[0];
const int C = (int)PyArray_DIMS(obj_ptr)[1];
const int R = (int)PyArray_DIMS(pyObj)[0];
const int C = (int)PyArray_DIMS(pyObj)[1];
if( (MatType::RowsAtCompileTime!=R)
&& (MatType::RowsAtCompileTime!=Eigen::Dynamic) )
......@@ -298,7 +336,7 @@ namespace eigenpy
}
// Check if the Scalar type of the obj_ptr is compatible with the Scalar type of MatType
if ((PyArray_ObjectType(reinterpret_cast<PyObject *>(obj_ptr), 0)) == NPY_INT)
if ((PyArray_ObjectType(reinterpret_cast<PyObject *>(pyObj), 0)) == NPY_INT)
{
if(!FromTypeToType<int,typename MatType::Scalar>::value)
{
......@@ -308,7 +346,7 @@ namespace eigenpy
return 0;
}
}
else if ((PyArray_ObjectType(reinterpret_cast<PyObject *>(obj_ptr), 0)) == NPY_LONG)
else if ((PyArray_ObjectType(reinterpret_cast<PyObject *>(pyObj), 0)) == NPY_LONG)
{
if(!FromTypeToType<long,typename MatType::Scalar>::value)
{
......@@ -318,7 +356,7 @@ namespace eigenpy
return 0;
}
}
else if ((PyArray_ObjectType(reinterpret_cast<PyObject *>(obj_ptr), 0)) == NPY_FLOAT)
else if ((PyArray_ObjectType(reinterpret_cast<PyObject *>(pyObj), 0)) == NPY_FLOAT)
{
if(!FromTypeToType<float,typename MatType::Scalar>::value)
{
......@@ -328,7 +366,7 @@ namespace eigenpy
return 0;
}
}
else if ((PyArray_ObjectType(reinterpret_cast<PyObject *>(obj_ptr), 0)) == NPY_DOUBLE)
else if ((PyArray_ObjectType(reinterpret_cast<PyObject *>(pyObj), 0)) == NPY_DOUBLE)
{
if(!FromTypeToType<double,typename MatType::Scalar>::value)
{
......@@ -338,7 +376,7 @@ namespace eigenpy
return 0;
}
}
else if ((PyArray_ObjectType(reinterpret_cast<PyObject *>(obj_ptr), 0))
else if ((PyArray_ObjectType(reinterpret_cast<PyObject *>(pyObj), 0))
!= NumpyEquivalentType<typename MatType::Scalar>::type_code)
{
#ifndef NDEBUG
......@@ -349,7 +387,7 @@ namespace eigenpy
}
#ifdef NPY_1_8_API_VERSION
if (!(PyArray_FLAGS(obj_ptr)))
if (!(PyArray_FLAGS(pyObj)))
#else
if (!(PyArray_FLAGS(obj_ptr) & NPY_ALIGNED))
#endif
......@@ -360,15 +398,13 @@ namespace eigenpy
return 0;
}
return obj_ptr;
return pyObj;
}
// Convert obj_ptr into an Eigen::Vector
/// \brief Allocate memory and copy pyObj in the new storage
static void construct(PyObject* pyObj,
bp::converter::rvalue_from_python_stage1_data* memory)
{
using namespace Eigen;
PyArrayObject * pyArray = reinterpret_cast<PyArrayObject*>(pyObj);
assert((PyArray_DIMS(pyArray)[0]<INT_MAX) && (PyArray_DIMS(pyArray)[1]<INT_MAX));
......@@ -379,6 +415,42 @@ namespace eigenpy
memory->convertible = storage;
}
static void registration()
{
bp::converter::registry::push_back
(reinterpret_cast<void *(*)(_object *)>(&EigenFromPy::convertible),
&EigenFromPy::construct,bp::type_id<MatType>());
}
};
template<typename MatType>
struct EigenFromPy< Eigen::MatrixBase<MatType> >
{
typedef EigenFromPy<MatType> EigenFromPyDerived;
typedef Eigen::MatrixBase<MatType> Base;
/// \brief Determine if pyObj can be converted into a MatType object
static void* convertible(PyArrayObject* pyObj)
{
std::cout << "call: EigenFromPy< Eigen::MatrixBase<MatType> >::convertible" << std::endl;
return EigenFromPyDerived::convertible(pyObj);
}
/// \brief Allocate memory and copy pyObj in the new storage
static void construct(PyObject* pyObj,
bp::converter::rvalue_from_python_stage1_data* memory)
{
std::cout << "call: EigenFromPy< Eigen::MatrixBase<MatType> >::construct" << std::endl;
EigenFromPyDerived::construct(pyObj,memory);
}
static void registration()
{
bp::converter::registry::push_back
(reinterpret_cast<void *(*)(_object *)>(&EigenFromPy::convertible),
&EigenFromPy::construct,bp::type_id<Base>());
}
};
#define numpy_import_array() {if (_import_array() < 0) {PyErr_Print(); PyErr_SetString(PyExc_ImportError, "numpy.core.multiarray failed to import"); } }
......@@ -394,14 +466,11 @@ namespace eigenpy
{
static void registration()
{
bp::converter::registry::push_back
(reinterpret_cast<void *(*)(_object *)>(&EigenFromPy<MatType>::convertible),
&EigenFromPy<MatType>::construct,bp::type_id<MatType>());
EigenFromPy<MatType>::registration();
// Add also conversion to Eigen::MatrixBase<MatType>
bp::converter::registry::push_back
(reinterpret_cast<void *(*)(_object *)>(&EigenFromPy<MatType>::convertible),
&EigenFromPy<MatType>::construct,bp::type_id< Eigen::MatrixBase<MatType> >());
typedef Eigen::MatrixBase<MatType> MatTypeBase;
EigenFromPy<MatTypeBase>::registration();
}
};
......@@ -419,7 +488,6 @@ namespace eigenpy
};
#endif
template<typename MatType>
void enableEigenPySpecific()
{
......
#ifndef __eigenpy_details_rvalue_from_python_data_hpp__
#define __eigenpy_details_rvalue_from_python_data_hpp__
#include <boost/python/converter/rvalue_from_python_data.hpp>
#include <Eigen/Core>
namespace boost
{
namespace python
{
namespace converter
{
/// \brief Template specialization of rvalue_from_python_data
template<typename Derived>
struct rvalue_from_python_data<Eigen::MatrixBase<Derived> const & >
: rvalue_from_python_storage<Eigen::MatrixBase<Derived> const & >
{
typedef Eigen::MatrixBase<Derived> const & T;
# if (!defined(__MWERKS__) || __MWERKS__ >= 0x3000) \
&& (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 245) \
&& (!defined(__DECCXX_VER) || __DECCXX_VER > 60590014) \
&& !defined(BOOST_PYTHON_SYNOPSIS) /* Synopsis' OpenCXX has trouble parsing this */
// This must always be a POD struct with m_data its first member.
BOOST_STATIC_ASSERT(BOOST_PYTHON_OFFSETOF(rvalue_from_python_storage<T>,stage1) == 0);
# endif
// The usual constructor
rvalue_from_python_data(rvalue_from_python_stage1_data const & _stage1)
{
this->stage1 = _stage1;
}
// This constructor just sets m_convertible -- used by
// implicitly_convertible<> to perform the final step of the
// conversion, where the construct() function is already known.
rvalue_from_python_data(void* convertible)
{
this->stage1.convertible = convertible;
}
// Destroys any object constructed in the storage.
~rvalue_from_python_data()
{
if (this->stage1.convertible == this->storage.bytes)
static_cast<Derived *>((void *)this->storage.bytes)->~Derived();
}
};
}
}
} // namespace boost::python::converter
#endif // ifndef __eigenpy_details_rvalue_from_python_data_hpp__
......@@ -9,19 +9,20 @@
#include "eigenpy/fwd.hpp"
#include "eigenpy/deprecated.hh"
#include "eigenpy/eigenpy_export.h"
#if EIGEN_VERSION_AT_LEAST(3,2,0)
#include "eigenpy/ref.hpp"
#define ENABLE_SPECIFIC_MATRIX_TYPE(TYPE) \
enableEigenPySpecific<TYPE>(); \
enableEigenPySpecific< eigenpy::Ref<TYPE> >();
::eigenpy::enableEigenPySpecific<TYPE>(); \
::eigenpy::enableEigenPySpecific< eigenpy::Ref<TYPE> >();
#else
#else // if EIGEN_VERSION_AT_LEAST(3,2,0)
#define ENABLE_SPECIFIC_MATRIX_TYPE(TYPE) \
enableEigenPySpecific<TYPE>();
::eigenpy::enableEigenPySpecific<TYPE>();
#endif
#endif // if EIGEN_VERSION_AT_LEAST(3,2,0)
namespace eigenpy
{
......
......@@ -55,8 +55,6 @@ namespace eigenpy
const int stride2 = (int)PyArray_STRIDE(pyArray, 1) / (int)itemsize;
Stride stride(stride2,stride1);
if( (MatType::RowsAtCompileTime!=R)
&& (MatType::RowsAtCompileTime!=Eigen::Dynamic) )
{ throw eigenpy::Exception("The number of rows does not fit with the matrix type."); }
......
......@@ -19,7 +19,8 @@ namespace eigenpy
{
template<typename PlainObjectTypeT>
struct Ref : Eigen::Ref<PlainObjectTypeT,EIGENPY_DEFAULT_ALIGNMENT_VALUE,typename StrideType<PlainObjectTypeT>::type>
struct Ref
: Eigen::Ref<PlainObjectTypeT,EIGENPY_DEFAULT_ALIGNMENT_VALUE,typename StrideType<PlainObjectTypeT>::type>
{
public:
typedef Eigen::Ref<PlainObjectTypeT,EIGENPY_DEFAULT_ALIGNMENT_VALUE,typename eigenpy::template StrideType<PlainObjectTypeT>::type> Base;
......
......@@ -19,6 +19,8 @@ ENDMACRO(SYMLINK_AND_INSTALL_HEADERS HEADERS SUBPATH)
# --- LIBRARY --- #
SET(PYWRAP ${PROJECT_NAME}_pywrap)
SET(PYWRAP ${PYWRAP} PARENT_SCOPE)
MAKE_DIRECTORY("${${PROJECT_NAME}_BINARY_DIR}/python/${PROJECT_NAME}")
ADD_LIBRARY(${PYWRAP} SHARED main.cpp)
......
......@@ -41,5 +41,9 @@ 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-switch" "unittest/python/test_switch.py" "python/eigenpy")
SET_TESTS_PROPERTIES("py-switch" PROPERTIES DEPENDS ${PYWRAP})
ADD_PYTHON_UNIT_TEST("py-dimensions" "unittest/python/test_dimensions.py" "python/eigenpy")
SET_TESTS_PROPERTIES("py-dimensions" PROPERTIES DEPENDS ${PYWRAP})
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment