Unverified Commit 56fe46a2 authored by Justin Carpentier's avatar Justin Carpentier Committed by GitHub
Browse files

Merge pull request #163 from jcarpent/devel

Implement full support of Eigen::Ref
parents bb8df999 9e606508
......@@ -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
......
......@@ -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> >
{
......
......@@ -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> >
......
......@@ -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