Add numpy_eigen as a 3rd party library.

This commit adds a simple version of numpy_eigen, copied from
gtborg/numpy_eigen commit 255c09efb82496, and with a fix released in the commit
9a75383733b3dc4bc2bb0649053949ad2bec9326 of Scheizer-Messer/numpy_eigen
(https://github.com/ethz-asl/Schweizer-Messer/tree/master/numpy_eigen)

Conflicts:
	CMakeLists.txt
	gtsam/CMakeLists.txt
release/4.3a0
Ellon Mendes 2015-11-11 21:39:08 +01:00
parent 6a34fa0a22
commit 2dbe7fa2e9
7 changed files with 878 additions and 4 deletions

View File

@ -30,7 +30,6 @@ message(STATUS "GTSAM_SOURCE_ROOT_DIR: [${GTSAM_SOURCE_ROOT_DIR}]")
# Load build type flags and default to Debug mode
include(GtsamBuildTypes)
include(GtsamPythonWrap)
# Use macros for creating tests/timing scripts
include(GtsamTesting)
@ -65,6 +64,7 @@ option(GTSAM_WITH_TBB "Use Intel Threaded Building Blocks (TB
option(GTSAM_WITH_EIGEN_MKL "Eigen will use Intel MKL if available" ON)
option(GTSAM_WITH_EIGEN_MKL_OPENMP "Eigen, when using Intel MKL, will also use OpenMP for multithreading if available" ON)
option(GTSAM_THROW_CHEIRALITY_EXCEPTION "Throw exception when a triangulated point is behind a camera" ON)
option(GTSAM_BUILD_PYTHON "Build Python wrapper statically (increases build time)" OFF)
# Options relating to MATLAB wrapper
# TODO: Check for matlab mex binary before handling building of binaries
@ -345,8 +345,10 @@ if (GTSAM_INSTALL_MATLAB_TOOLBOX)
add_subdirectory(matlab)
endif()
if(GTSAM_BUILD_PYTHON)
add_subdirectory(python)
# Python wrap
if (GTSAM_BUILD_PYTHON)
include(GtsamPythonWrap)
wrap_and_install_python(gtsampy.h "${GTSAM_ADDITIONAL_LIBRARIES}" "")
endif()
# Build gtsam_unstable

View File

@ -1,5 +1,4 @@
#Setup cache options
option(GTSAM_BUILD_PYTHON "Build Python wrapper statically (increases build time)" OFF)
set(GTSAM_BUILD_PYTHON_FLAGS "" CACHE STRING "Extra flags for running Matlab PYTHON compilation")
set(GTSAM_PYTHON_INSTALL_PATH "" CACHE PATH "Python toolbox destination, blank defaults to CMAKE_INSTALL_PREFIX/borg/python")
if(NOT GTSAM_PYTHON_INSTALL_PATH)

6
gtsam/3rdparty/numpy_eigen/README.md vendored Normal file
View File

@ -0,0 +1,6 @@
numpy_eigen
===========
A boost python converter that handles the copy of matrices from the Eigen linear algebra library in C++ to numpy in Python.
This is a minimal version based on the original from Paul Furgale (https://github.com/ethz-asl/Schweizer-Messer/tree/master/numpy_eigen)

View File

@ -0,0 +1,316 @@
/**
* @file NumpyEigenConverter.hpp
* @author Paul Furgale <paul.furgale@utoronto.ca>
* @date Fri Feb 4 11:17:25 2011
*
* @brief Classes to support conversion from numpy arrays in Python
* to Eigen3 matrices in c++
*
*
*/
#ifndef NUMPY_EIGEN_CONVERTER_HPP
#define NUMPY_EIGEN_CONVERTER_HPP
#include "boost_python_headers.hpp"
#include <iostream>
#define PY_ARRAY_UNIQUE_SYMBOL NP_Eigen_AS
#include <numpy/arrayobject.h>
#include "type_traits.hpp"
#include <boost/lexical_cast.hpp>
#include "copy_routines.hpp"
/**
* @class NumpyEigenConverter
* @tparam the Eigen3 matrix type this class is specialized for
*
* adapted from http://misspent.wordpress.com/2009/09/27/how-to-write-boost-python-converters/
* General help available http://docs.scipy.org/doc/numpy/reference/c-api.array.html
*
* To use:
*
* #include <NumpyEigenConverter.hpp>
*
*
* BOOST_PYTHON_MODULE(libmy_module_python)
* {
* // The converters will cause a segfault unless import_array() is called before the first one
* import_array();
* NumpyEigenConverter<Eigen::Matrix< double, 1, 1 > >::register_converter();
* NumpyEigenConverter<Eigen::Matrix< double, 2, 1 > >::register_converter();
* }
*
*/
template<typename EIGEN_MATRIX_T>
struct NumpyEigenConverter
{
typedef EIGEN_MATRIX_T matrix_t;
typedef typename matrix_t::Scalar scalar_t;
enum {
RowsAtCompileTime = matrix_t::RowsAtCompileTime,
ColsAtCompileTime = matrix_t::ColsAtCompileTime,
MaxRowsAtCompileTime = matrix_t::MaxRowsAtCompileTime,
MaxColsAtCompileTime = matrix_t::MaxColsAtCompileTime,
NpyType = TypeToNumPy<scalar_t>::NpyType,
//Flags = ei_compute_matrix_flags<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols>::ret,
//CoeffReadCost = NumTraits<Scalar>::ReadCost,
Options = matrix_t::Options
//InnerStrideAtCompileTime = 1,
//OuterStrideAtCompileTime = (Options&RowMajor) ? ColsAtCompileTime : RowsAtCompileTime
};
static std::string castSizeOption(int option)
{
if(option == Eigen::Dynamic)
return "Dynamic";
else
return boost::lexical_cast<std::string>(option);
}
static std::string toString()
{
return std::string() + "Eigen::Matrix<" + TypeToNumPy<scalar_t>::typeString() + ", " +
castSizeOption(RowsAtCompileTime) + ", " +
castSizeOption(ColsAtCompileTime) + ", " +
boost::lexical_cast<std::string>((int)Options) + ", " +
castSizeOption(MaxRowsAtCompileTime) + ", " +
castSizeOption(MaxColsAtCompileTime) + ">";
}
// The "Convert from C to Python" API
static PyObject * convert(const matrix_t & M)
{
PyObject * P = NULL;
if(RowsAtCompileTime == 1 || ColsAtCompileTime == 1)
{
// Create a 1D array
npy_intp dimensions[1];
dimensions[0] = M.size();
P = PyArray_SimpleNew(1, dimensions, TypeToNumPy<scalar_t>::NpyType);
numpyTypeDemuxer< CopyEigenToNumpyVector<const matrix_t> >(&M,P);
}
else
{
// create a 2D array.
npy_intp dimensions[2];
dimensions[0] = M.rows();
dimensions[1] = M.cols();
P = PyArray_SimpleNew(2, dimensions, TypeToNumPy<scalar_t>::NpyType);
numpyTypeDemuxer< CopyEigenToNumpyMatrix<const matrix_t> >(&M,P);
}
// incrementing the reference seems to cause a memory leak.
// boost::python::incref(P);
// This agrees with the sample code found here:
// http://mail.python.org/pipermail/cplusplus-sig/2008-October/013825.html
return P;
}
static bool isDimensionValid(int requestedSize, int sizeAtCompileTime, int maxSizeAtCompileTime)
{
bool valid = true;
if(sizeAtCompileTime == Eigen::Dynamic)
{
// Check for dynamic fixed size
// http://eigen.tuxfamily.org/dox-devel/TutorialMatrixClass.html#TutorialMatrixOptTemplParams
if(!(maxSizeAtCompileTime == Eigen::Dynamic || requestedSize <= maxSizeAtCompileTime))
{
valid = false;
}
}
else if(sizeAtCompileTime != requestedSize)
{
valid = false;
}
return valid;
}
static void checkMatrixSizes(PyObject * obj_ptr)
{
int rows = PyArray_DIM(obj_ptr, 0);
int cols = PyArray_DIM(obj_ptr, 1);
bool rowsValid = isDimensionValid(rows, RowsAtCompileTime, MaxRowsAtCompileTime);
bool colsValid = isDimensionValid(cols, ColsAtCompileTime, MaxColsAtCompileTime);
if(!rowsValid || !colsValid)
{
THROW_TYPE_ERROR("Can not convert " << npyArrayTypeString(obj_ptr) << " to " << toString()
<< ". Mismatched sizes.");
}
}
static void checkRowVectorSizes(PyObject * obj_ptr, int cols)
{
if(!isDimensionValid(cols, ColsAtCompileTime, MaxColsAtCompileTime))
{
THROW_TYPE_ERROR("Can not convert " << npyArrayTypeString(obj_ptr) << " to " << toString()
<< ". Mismatched sizes.");
}
}
static void checkColumnVectorSizes(PyObject * obj_ptr, int rows)
{
// Check if the type can accomidate one column.
if(ColsAtCompileTime == Eigen::Dynamic || ColsAtCompileTime == 1)
{
if(!isDimensionValid(rows, RowsAtCompileTime, MaxRowsAtCompileTime))
{
THROW_TYPE_ERROR("Can not convert " << npyArrayTypeString(obj_ptr) << " to " << toString()
<< ". Mismatched sizes.");
}
}
else
{
THROW_TYPE_ERROR("Can not convert " << npyArrayTypeString(obj_ptr) << " to " << toString()
<< ". Mismatched sizes.");
}
}
static void checkVectorSizes(PyObject * obj_ptr)
{
int size = PyArray_DIM(obj_ptr, 0);
// If the number of rows is fixed at 1, assume that is the sense of the vector.
// Otherwise, assume it is a column.
if(RowsAtCompileTime == 1)
{
checkRowVectorSizes(obj_ptr, size);
}
else
{
checkColumnVectorSizes(obj_ptr, size);
}
}
static void* convertible(PyObject *obj_ptr)
{
// Check for a null pointer.
if(!obj_ptr)
{
//THROW_TYPE_ERROR("PyObject pointer was null");
return 0;
}
// Make sure this is a numpy array.
if (!PyArray_Check(obj_ptr))
{
//THROW_TYPE_ERROR("Conversion is only defined for numpy array and matrix types");
return 0;
}
// Check the type of the array.
int npyType = PyArray_ObjectType(obj_ptr, 0);
if(!TypeToNumPy<scalar_t>::canConvert(npyType))
{
//THROW_TYPE_ERROR("Can not convert " << npyArrayTypeString(obj_ptr) << " to " << toString()
// << ". Mismatched types.");
return 0;
}
// Check the array dimensions.
int nd = PyArray_NDIM(obj_ptr);
if(nd != 1 && nd != 2)
{
THROW_TYPE_ERROR("Conversion is only valid for arrays with 1 or 2 dimensions. Argument has " << nd << " dimensions");
}
if(nd == 1)
{
checkVectorSizes(obj_ptr);
}
else
{
// Two-dimensional matrix type.
checkMatrixSizes(obj_ptr);
}
return obj_ptr;
}
static void construct(PyObject *obj_ptr, boost::python::converter::rvalue_from_python_stage1_data *data)
{
boost::python::converter::rvalue_from_python_storage<matrix_t> * matData = reinterpret_cast<boost::python::converter::rvalue_from_python_storage<matrix_t> * >(data);
void* storage = matData->storage.bytes;
// Make sure storage is 16byte aligned. With help from code from Memory.h
void * aligned = reinterpret_cast<void*>((reinterpret_cast<size_t>(storage) & ~(size_t(15))) + 16);
matrix_t * Mp = new (aligned) matrix_t();
// Stash the memory chunk pointer for later use by boost.python
// This signals boost::python that the new value must be deleted eventually
data->convertible = storage;
// std::cout << "Creating aligned pointer " << aligned << " from storage " << storage << std::endl;
// std::cout << "matrix size: " << sizeof(matrix_t) << std::endl;
// std::cout << "referent size: " << boost::python::detail::referent_size< matrix_t & >::value << std::endl;
// std::cout << "sizeof(storage): " << sizeof(matData->storage) << std::endl;
// std::cout << "sizeof(bytes): " << sizeof(matData->storage.bytes) << std::endl;
matrix_t & M = *Mp;
int nd = PyArray_NDIM(obj_ptr);
if(nd == 1)
{
int size = PyArray_DIM(obj_ptr, 0);
// This is a vector type
if(RowsAtCompileTime == 1)
{
// Row Vector
M.resize(1,size);
}
else
{
// Column Vector
M.resize(size,1);
}
numpyTypeDemuxer< CopyNumpyToEigenVector<matrix_t> >(&M,obj_ptr);
}
else
{
int rows = PyArray_DIM(obj_ptr, 0);
int cols = PyArray_DIM(obj_ptr, 1);
M.resize(rows,cols);
numpyTypeDemuxer< CopyNumpyToEigenMatrix<matrix_t> >(&M,obj_ptr);
}
}
// The registration function.
static void register_converter()
{
boost::python::to_python_converter<matrix_t,NumpyEigenConverter>();
boost::python::converter::registry::push_back(
&NumpyEigenConverter::convertible,
&NumpyEigenConverter::construct,
boost::python::type_id<matrix_t>());
}
};
#endif /* NUMPY_EIGEN_CONVERTER_HPP */

View File

@ -0,0 +1,250 @@
/**
* @file boost_python_headers.hpp
* @author Paul Furgale <paul.furgale@gmail.com>
* @date Mon Dec 12 10:36:03 2011
*
* @brief A header that specializes boost-python to work with fixed-sized Eigen types.
*
* The original version of this library did not include these specializations and this caused
* assert failures when running on Ubuntu 10.04 32-bit. More information about fixed-size
* vectorizable types in Eigen is available here:
* http://eigen.tuxfamily.org/dox-devel/TopicFixedSizeVectorizable.html
*
* This code has been tested on Ubunutu 10.04 64 and 32 bit, OSX Snow Leopard and OSX Lion.
*
* This code is derived from boost/python/converter/arg_from_python.hpp
* Copyright David Abrahams 2002.
* Distributed under the Boost Software License, Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt)
*
*/
#ifndef NUMPY_EIGEN_CONVERTERS_HPP
#define NUMPY_EIGEN_CONVERTERS_HPP
#include <Eigen/Core>
#include <boost/python.hpp>
#include <boost/python/detail/referent_storage.hpp>
#include <boost/python/converter/arg_from_python.hpp>
#include <boost/python/converter/rvalue_from_python_data.hpp>
#include <boost/python/tuple.hpp>
namespace boost { namespace python { namespace detail {
template<typename T>
struct referent_size;
// This bit of code makes sure we have 16 extra bytes to do the pointer alignment for fixed-sized Eigen types
template<typename T, int A, int B, int C, int D, int E>
struct referent_size< Eigen::Matrix<T,A,B,C,D,E>& >
{
// Add 16 bytes so we can get alignment
BOOST_STATIC_CONSTANT( std::size_t, value = sizeof(Eigen::Matrix<T,A,B,C,D,E>) + 16);
};
// This bit of code makes sure we have 16 extra bytes to do the pointer alignment for fixed-sized Eigen types
template<typename T, int A, int B, int C, int D, int E>
struct referent_size< Eigen::Matrix<T,A,B,C,D,E> const & >
{
// Add 16 bytes so we can get alignment
BOOST_STATIC_CONSTANT( std::size_t, value = sizeof(Eigen::Matrix<T,A,B,C,D,E>) + 16);
};
// This bit of code makes sure we have 16 extra bytes to do the pointer alignment for fixed-sized Eigen types
template<typename T, int A, int B, int C, int D, int E>
struct referent_size< Eigen::Matrix<T,A,B,C,D,E> >
{
// Add 16 bytes so we can get alignment
BOOST_STATIC_CONSTANT( std::size_t, value = sizeof(Eigen::Matrix<T,A,B,C,D,E>) + 16);
};
}}}
namespace boost { namespace python { namespace converter {
template<typename S, int A, int B, int C, int D, int E>
struct rvalue_from_python_data< Eigen::Matrix<S,A,B,C,D,E> const &> : rvalue_from_python_storage< Eigen::Matrix<S,A,B,C,D,E> const & >
{
typedef typename Eigen::Matrix<S,A,B,C,D,E> 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()
{
// Realign the pointer and destroy
if (this->stage1.convertible == this->storage.bytes)
{
void * storage = reinterpret_cast<void *>(this->storage.bytes);
T * aligned = reinterpret_cast<T *>(reinterpret_cast<void *>((reinterpret_cast<size_t>(storage) & ~(size_t(15))) + 16));
//std::cout << "Destroying " << (void*)aligned << std::endl;
aligned->T::~T();
}
}
private:
typedef typename add_reference<typename add_cv<T>::type>::type ref_type;
};
// Used when T is a plain value (non-pointer, non-reference) type or
// a (non-volatile) const reference to a plain value type.
template<typename S, int A, int B, int C, int D, int E>
struct arg_rvalue_from_python< Eigen::Matrix<S,A,B,C,D,E> >
{
typedef Eigen::Matrix<S,A,B,C,D,E> const & T;
typedef typename boost::add_reference<
T
// We can't add_const here, or it would be impossible to pass
// auto_ptr<U> args from Python to C++
>::type result_type;
arg_rvalue_from_python(PyObject * obj) : m_data(converter::rvalue_from_python_stage1(obj, registered<T>::converters))
, m_source(obj)
{
}
bool convertible() const
{
return m_data.stage1.convertible != 0;
}
# if BOOST_MSVC < 1301 || _MSC_FULL_VER > 13102196
typename arg_rvalue_from_python<T>::
# endif
result_type operator()()
{
if (m_data.stage1.construct != 0)
m_data.stage1.construct(m_source, &m_data.stage1);
// Here is the magic...
// Realign the pointer
void * storage = reinterpret_cast<void *>(m_data.storage.bytes);
void * aligned = reinterpret_cast<void*>((reinterpret_cast<size_t>(storage) & ~(size_t(15))) + 16);
return python::detail::void_ptr_to_reference(aligned, (result_type(*)())0);
}
private:
rvalue_from_python_data<result_type> m_data;
PyObject* m_source;
};
// Used when T is a plain value (non-pointer, non-reference) type or
// a (non-volatile) const reference to a plain value type.
template<typename S, int A, int B, int C, int D, int E>
struct arg_rvalue_from_python< Eigen::Matrix<S,A,B,C,D,E> const & >
{
typedef Eigen::Matrix<S,A,B,C,D,E> const & T;
typedef typename boost::add_reference<
T
// We can't add_const here, or it would be impossible to pass
// auto_ptr<U> args from Python to C++
>::type result_type;
arg_rvalue_from_python(PyObject * obj) : m_data(converter::rvalue_from_python_stage1(obj, registered<T>::converters))
, m_source(obj)
{
}
bool convertible() const
{
return m_data.stage1.convertible != 0;
}
# if BOOST_MSVC < 1301 || _MSC_FULL_VER > 13102196
typename arg_rvalue_from_python<T>::
# endif
result_type operator()()
{
if (m_data.stage1.construct != 0)
m_data.stage1.construct(m_source, &m_data.stage1);
// Here is the magic...
// Realign the pointer
void * storage = reinterpret_cast<void *>(m_data.storage.bytes);
void * aligned = reinterpret_cast<void*>((reinterpret_cast<size_t>(storage) & ~(size_t(15))) + 16);
return python::detail::void_ptr_to_reference(aligned, (result_type(*)())0);
}
private:
rvalue_from_python_data<result_type> m_data;
PyObject* m_source;
};
// Used when T is a plain value (non-pointer, non-reference) type or
// a (non-volatile) const reference to a plain value type.
template<typename S, int A, int B, int C, int D, int E>
struct arg_rvalue_from_python< Eigen::Matrix<S,A,B,C,D,E> const >
{
typedef Eigen::Matrix<S,A,B,C,D,E> const & T;
typedef typename boost::add_reference<
T
// We can't add_const here, or it would be impossible to pass
// auto_ptr<U> args from Python to C++
>::type result_type;
arg_rvalue_from_python(PyObject * obj) : m_data(converter::rvalue_from_python_stage1(obj, registered<T>::converters))
, m_source(obj)
{
}
bool convertible() const
{
return m_data.stage1.convertible != 0;
}
# if BOOST_MSVC < 1301 || _MSC_FULL_VER > 13102196
typename arg_rvalue_from_python<T>::
# endif
result_type operator()()
{
if (m_data.stage1.construct != 0)
m_data.stage1.construct(m_source, &m_data.stage1);
// Here is the magic...
// Realign the pointer
void * storage = reinterpret_cast<void *>(m_data.storage.bytes);
void * aligned = reinterpret_cast<void*>((reinterpret_cast<size_t>(storage) & ~(size_t(15))) + 16);
return python::detail::void_ptr_to_reference(aligned, (result_type(*)())0);
}
private:
rvalue_from_python_data<result_type> m_data;
PyObject* m_source;
};
}}}
#endif /* NUMPY_EIGEN_CONVERTERS_HPP */

View File

@ -0,0 +1,148 @@
#ifndef NUMPY_EIGEN_COPY_ROUTINES_HPP
#define NUMPY_EIGEN_COPY_ROUTINES_HPP
template<typename EIGEN_T>
struct CopyNumpyToEigenMatrix
{
typedef EIGEN_T matrix_t;
typedef typename matrix_t::Scalar scalar_t;
template<typename T>
void exec(EIGEN_T * M_, PyObject * P_)
{
// Assumes M is already initialized.
for(int r = 0; r < M_->rows(); r++)
{
for(int c = 0; c < M_->cols(); c++)
{
T * p = static_cast<T*>(PyArray_GETPTR2(P_, r, c));
(*M_)(r,c) = static_cast<scalar_t>(*p);
}
}
}
};
template<typename EIGEN_T>
struct CopyEigenToNumpyMatrix
{
typedef EIGEN_T matrix_t;
typedef typename matrix_t::Scalar scalar_t;
template<typename T>
void exec(EIGEN_T * M_, PyObject * P_)
{
// Assumes M is already initialized.
for(int r = 0; r < M_->rows(); r++)
{
for(int c = 0; c < M_->cols(); c++)
{
T * p = static_cast<T*>(PyArray_GETPTR2(P_, r, c));
*p = static_cast<T>((*M_)(r,c));
}
}
}
};
template<typename EIGEN_T>
struct CopyEigenToNumpyVector
{
typedef EIGEN_T matrix_t;
typedef typename matrix_t::Scalar scalar_t;
template<typename T>
void exec(EIGEN_T * M_, PyObject * P_)
{
// Assumes M is already initialized.
for(int i = 0; i < M_->size(); i++)
{
T * p = static_cast<T*>(PyArray_GETPTR1(P_, i));
*p = static_cast<T>((*M_)(i));
}
}
};
template<typename EIGEN_T>
struct CopyNumpyToEigenVector
{
typedef EIGEN_T matrix_t;
typedef typename matrix_t::Scalar scalar_t;
template<typename T>
void exec(EIGEN_T * M_, PyObject * P_)
{
// Assumes M is already initialized.
for(int i = 0; i < M_->size(); i++)
{
T * p = static_cast<T*>(PyArray_GETPTR1(P_, i));
(*M_)(i) = static_cast<scalar_t>(*p);
}
}
};
// Crazy syntax in this function was found here:
// http://stackoverflow.com/questions/1840253/c-template-member-function-of-template-class-called-from-template-function/1840318#1840318
template< typename FUNCTOR_T>
inline void numpyTypeDemuxer(typename FUNCTOR_T::matrix_t * M, PyObject * P)
{
FUNCTOR_T f;
int npyType = PyArray_ObjectType(P, 0);
switch(npyType)
{
case NPY_BOOL:
f.template exec<bool>(M,P);
break;
case NPY_BYTE:
f.template exec<char>(M,P);
break;
case NPY_UBYTE:
f.template exec<unsigned char>(M,P);
break;
case NPY_SHORT:
f.template exec<short>(M,P);
break;
case NPY_USHORT:
f.template exec<unsigned short>(M,P);
break;
case NPY_INT:
f.template exec<int>(M,P);
break;
case NPY_UINT:
f.template exec<unsigned int>(M,P);
break;
case NPY_LONG:
f.template exec<long>(M,P);
break;
case NPY_ULONG:
f.template exec<unsigned long>(M,P);
break;
case NPY_LONGLONG:
f.template exec<long long>(M,P);
break;
case NPY_ULONGLONG:
f.template exec<unsigned long long>(M,P);
break;
case NPY_FLOAT:
f.template exec<float>(M,P);
break;
case NPY_DOUBLE:
f.template exec<double>(M,P);
break;
case NPY_LONGDOUBLE:
f.template exec<long double>(M,P);
break;
default:
THROW_TYPE_ERROR("Unsupported type: " << npyTypeToString(npyType));
}
}
#endif /* NUMPY_EIGEN_COPY_ROUTINES_HPP */

View File

@ -0,0 +1,153 @@
#ifndef NUMPY_EIGEN_TYPE_TRAITS_HPP
#define NUMPY_EIGEN_TYPE_TRAITS_HPP
#define THROW_TYPE_ERROR(msg) \
{ \
std::stringstream type_error_ss; \
type_error_ss << msg; \
PyErr_SetString(PyExc_TypeError, type_error_ss.str().c_str()); \
throw boost::python::error_already_set(); \
}
////////////////////////////////////////////////
// TypeToNumPy
// Defines helper functions based on the Eigen3 matrix type that
// decide what conversions can happen Eigen3 --> NumPy
// Also, converts a type to a NumPy enum.
template<typename Scalar> struct TypeToNumPy;
template<> struct TypeToNumPy<int>
{
enum { NpyType = NPY_INT };
static const char * npyString() { return "NPY_INT"; }
static const char * typeString() { return "int"; }
static bool canConvert(int type)
{
return type == NPY_INT || type == NPY_LONG;
}
};
template<> struct TypeToNumPy<unsigned char>
{
enum { NpyType = NPY_UBYTE };
static const char * npyString() { return "NPY_UBYTE"; }
static const char * typeString() { return "unsigned char"; }
static bool canConvert(int type)
{
return type == NPY_UBYTE || type == NPY_BYTE || type == NPY_CHAR;
}
};
template<> struct TypeToNumPy<char>
{
enum { NpyType = NPY_BYTE };
static const char * npyString() { return "NPY_BYTE"; }
static const char * typeString() { return "char"; }
static bool canConvert(int type)
{
return type == NPY_UBYTE || type == NPY_BYTE || type == NPY_CHAR;
}
};
template<> struct TypeToNumPy<float>
{
enum { NpyType = NPY_FLOAT };
static const char * npyString() { return "NPY_FLOAT"; }
static const char * typeString() { return "float"; }
static bool canConvert(int type)
{
return type == NPY_INT || type == NPY_FLOAT || type == NPY_LONG;
}
};
template<> struct TypeToNumPy<double>
{
enum { NpyType = NPY_DOUBLE };
static const char * npyString() { return "NPY_DOUBLE"; }
static const char * typeString() { return "double"; }
static bool canConvert(int type)
{
return type == NPY_INT || type == NPY_FLOAT || type == NPY_DOUBLE || type == NPY_LONG;
}
};
inline const char * npyTypeToString(int npyType)
{
switch(npyType)
{
case NPY_BOOL:
return "NPY_BOOL";
case NPY_BYTE:
return "NPY_BYTE";
case NPY_UBYTE:
return "NPY_UBYTE";
case NPY_SHORT:
return "NPY_SHORT";
case NPY_USHORT:
return "NPY_USHORT";
case NPY_INT:
return "NPY_INT";
case NPY_UINT:
return "NPY_UINT";
case NPY_LONG:
return "NPY_LONG";
case NPY_ULONG:
return "NPY_ULONG";
case NPY_LONGLONG:
return "NPY_LONGLONG";
case NPY_ULONGLONG:
return "NPY_ULONGLONG";
case NPY_FLOAT:
return "NPY_FLOAT";
case NPY_DOUBLE:
return "NPY_DOUBLE";
case NPY_LONGDOUBLE:
return "NPY_LONGDOUBLE";
case NPY_CFLOAT:
return "NPY_CFLOAT";
case NPY_CDOUBLE:
return "NPY_CDOUBLE";
case NPY_CLONGDOUBLE:
return "NPY_CLONGDOUBLE";
case NPY_OBJECT:
return "NPY_OBJECT";
case NPY_STRING:
return "NPY_STRING";
case NPY_UNICODE:
return "NPY_UNICODE";
case NPY_VOID:
return "NPY_VOID";
case NPY_NTYPES:
return "NPY_NTYPES";
case NPY_NOTYPE:
return "NPY_NOTYPE";
case NPY_CHAR:
return "NPY_CHAR";
default:
return "Unknown type";
}
}
inline std::string npyArrayTypeString(PyObject * obj_ptr)
{
std::stringstream ss;
int nd = PyArray_NDIM(obj_ptr);
ss << "numpy.array<" << npyTypeToString(PyArray_ObjectType(obj_ptr, 0)) << ">[";
if(nd > 0)
{
ss << PyArray_DIM(obj_ptr, 0);
for(int i = 1; i < nd; i++)
{
ss << ", " << PyArray_DIM(obj_ptr, i);
}
}
ss << "]";
return ss.str();
}
#endif /* NUMPY_EIGEN_TYPE_TRAITS_HPP */