Skip to content

Commit 5e6ac76

Browse files
authored
Merge pull request #211 from jcarpent/devel
Add support for exporting Eigen::Ref to Python
2 parents abcc7c3 + f15dab9 commit 5e6ac76

File tree

8 files changed

+165
-5
lines changed

8 files changed

+165
-5
lines changed

include/eigenpy/details.hpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,20 @@ namespace boost { namespace python { namespace detail {
4949
std::size_t, value = sizeof(MatType));
5050
};
5151

52+
template<class MatType>
53+
struct referent_size<Eigen::PlainObjectBase<MatType>&>
54+
{
55+
BOOST_STATIC_CONSTANT(
56+
std::size_t, value = sizeof(MatType));
57+
};
58+
59+
template<class MatType>
60+
struct referent_size<Eigen::PlainObjectBase<MatType> >
61+
{
62+
BOOST_STATIC_CONSTANT(
63+
std::size_t, value = sizeof(MatType));
64+
};
65+
5266
}}}
5367

5468
namespace eigenpy
@@ -65,7 +79,13 @@ namespace eigenpy
6579
{
6680
if(check_registration<MatType>()) return;
6781

82+
// to-python
6883
EigenToPyConverter<MatType>::registration();
84+
#if EIGEN_VERSION_AT_LEAST(3,2,0)
85+
EigenToPyConverter< Eigen::Ref<MatType> >::registration();
86+
#endif
87+
88+
// from-python
6989
EigenFromPyConverter<MatType>::registration();
7090
}
7191

include/eigenpy/eigen-from-python.hpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,14 @@ namespace boost { namespace python { namespace converter {
173173
EIGENPY_RVALUE_FROM_PYTHON_DATA_INIT(Derived const &)
174174
};
175175

176+
/// \brief Template specialization of rvalue_from_python_data
177+
template<typename Derived>
178+
struct rvalue_from_python_data<Eigen::PlainObjectBase<Derived> const &>
179+
: rvalue_from_python_data_eigen<Derived const &>
180+
{
181+
EIGENPY_RVALUE_FROM_PYTHON_DATA_INIT(Derived const &)
182+
};
183+
176184
template<typename MatType, int Options, typename Stride>
177185
struct rvalue_from_python_data<Eigen::Ref<MatType,Options,Stride> &>
178186
: rvalue_from_python_storage<Eigen::Ref<MatType,Options,Stride> &>
@@ -425,6 +433,10 @@ namespace eigenpy
425433
typedef Eigen::EigenBase<MatType> EigenBase;
426434
EigenFromPy<EigenBase>::registration();
427435

436+
// Add conversion to Eigen::PlainObjectBase<MatType>
437+
typedef Eigen::PlainObjectBase<MatType> PlainObjectBase;
438+
EigenFromPy<PlainObjectBase>::registration();
439+
428440
#if EIGEN_VERSION_AT_LEAST(3,2,0)
429441
// Add conversion to Eigen::Ref<MatType>
430442
typedef Eigen::Ref<MatType> RefType;
@@ -464,6 +476,20 @@ namespace eigenpy
464476
&EigenFromPy::construct,bp::type_id<Base>());
465477
}
466478
};
479+
480+
template<typename MatType>
481+
struct EigenFromPy< Eigen::PlainObjectBase<MatType> > : EigenFromPy<MatType>
482+
{
483+
typedef EigenFromPy<MatType> EigenFromPyDerived;
484+
typedef Eigen::PlainObjectBase<MatType> Base;
485+
486+
static void registration()
487+
{
488+
bp::converter::registry::push_back
489+
(reinterpret_cast<void *(*)(_object *)>(&EigenFromPy::convertible),
490+
&EigenFromPy::construct,bp::type_id<Base>());
491+
}
492+
};
467493

468494
#if EIGEN_VERSION_AT_LEAST(3,2,0)
469495

include/eigenpy/eigen-to-python.hpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,36 @@ namespace eigenpy
8282
}
8383
};
8484

85+
template<typename MatType, int Options, typename Stride>
86+
struct EigenToPy< Eigen::Ref<MatType,Options,Stride> >
87+
{
88+
static PyObject* convert(const Eigen::Ref<MatType,Options,Stride> & mat)
89+
{
90+
typedef Eigen::Ref<MatType,Options,Stride> EigenRef;
91+
92+
assert( (mat.rows()<INT_MAX) && (mat.cols()<INT_MAX)
93+
&& "Matrix range larger than int ... should never happen." );
94+
const npy_intp R = (npy_intp)mat.rows(), C = (npy_intp)mat.cols();
95+
96+
PyArrayObject* pyArray;
97+
// Allocate Python memory
98+
if( ( ((!(C == 1) != !(R == 1)) && !MatType::IsVectorAtCompileTime) || MatType::IsVectorAtCompileTime)
99+
&& NumpyType::getType() == ARRAY_TYPE) // Handle array with a single dimension
100+
{
101+
npy_intp shape[1] = { C == 1 ? R : C };
102+
pyArray = NumpyAllocator<EigenRef>::allocate(const_cast<EigenRef &>(mat),1,shape);
103+
}
104+
else
105+
{
106+
npy_intp shape[2] = { R,C };
107+
pyArray = NumpyAllocator<EigenRef>::allocate(const_cast<EigenRef &>(mat),2,shape);
108+
}
109+
110+
// Create an instance (either np.array or np.matrix)
111+
return NumpyType::make(pyArray).ptr();
112+
}
113+
};
114+
85115
template<typename MatType>
86116
struct EigenToPyConverter
87117
{

include/eigenpy/numpy-allocator.hpp

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,16 +57,41 @@ namespace eigenpy
5757
}
5858
else
5959
{
60-
return NumpyAllocator<MatType>::allocate(mat.derived(),nd,shape);
60+
return NumpyAllocator<MatType>::allocate(mat,nd,shape);
6161
}
6262
}
6363
};
6464

6565
#if EIGEN_VERSION_AT_LEAST(3,2,0)
6666

6767
template<typename MatType, int Options, typename Stride>
68-
struct NumpyAllocator<Eigen::Ref<MatType,Options,Stride> > : NumpyAllocator<MatType &>
68+
struct NumpyAllocator<Eigen::Ref<MatType,Options,Stride> >
6969
{
70+
typedef Eigen::Ref<MatType,Options,Stride> RefType;
71+
72+
static PyArrayObject * allocate(RefType & mat,
73+
npy_intp nd, npy_intp * shape)
74+
{
75+
typedef typename RefType::Scalar Scalar;
76+
enum { NPY_ARRAY_MEMORY_CONTIGUOUS = RefType::IsRowMajor ? NPY_ARRAY_CARRAY : NPY_ARRAY_FARRAY };
77+
78+
if(NumpyType::sharedMemory())
79+
{
80+
const int Scalar_type_code = Register::getTypeCode<Scalar>();
81+
PyArrayObject * pyArray = (PyArrayObject*) call_PyArray_New(getPyArrayType(),
82+
static_cast<int>(nd),
83+
shape,
84+
Scalar_type_code,
85+
mat.data(),
86+
NPY_ARRAY_MEMORY_CONTIGUOUS | NPY_ARRAY_ALIGNED);
87+
88+
return pyArray;
89+
}
90+
else
91+
{
92+
return NumpyAllocator<MatType>::allocate(mat,nd,shape);
93+
}
94+
}
7095
};
7196

7297
#endif
@@ -88,23 +113,49 @@ namespace eigenpy
88113
static_cast<int>(nd),
89114
shape,
90115
Scalar_type_code,
91-
const_cast<SimilarMatrixType &>(mat.derived()).data(),
116+
const_cast<Scalar *>(mat.data()),
92117
NPY_ARRAY_MEMORY_CONTIGUOUS_RO | NPY_ARRAY_ALIGNED);
93118

94119
return pyArray;
95120
}
96121
else
97122
{
98-
return NumpyAllocator<MatType>::allocate(mat.derived(),nd,shape);
123+
return NumpyAllocator<MatType>::allocate(mat,nd,shape);
99124
}
100125
}
101126
};
102127

103128
#if EIGEN_VERSION_AT_LEAST(3,2,0)
104129

105130
template<typename MatType, int Options, typename Stride>
106-
struct NumpyAllocator<const Eigen::Ref<const MatType,Options,Stride> > : NumpyAllocator<const MatType &>
131+
struct NumpyAllocator<const Eigen::Ref<const MatType,Options,Stride> >
107132
{
133+
typedef const Eigen::Ref<const MatType,Options,Stride> RefType;
134+
135+
template<typename SimilarMatrixType>
136+
static PyArrayObject * allocate(RefType & mat,
137+
npy_intp nd, npy_intp * shape)
138+
{
139+
typedef typename SimilarMatrixType::Scalar Scalar;
140+
enum { NPY_ARRAY_MEMORY_CONTIGUOUS_RO = SimilarMatrixType::IsRowMajor ? NPY_ARRAY_CARRAY_RO : NPY_ARRAY_FARRAY_RO };
141+
142+
if(NumpyType::sharedMemory())
143+
{
144+
const int Scalar_type_code = Register::getTypeCode<Scalar>();
145+
PyArrayObject * pyArray = (PyArrayObject*) call_PyArray_New(getPyArrayType(),
146+
static_cast<int>(nd),
147+
shape,
148+
Scalar_type_code,
149+
const_cast<Scalar *>(mat.data()),
150+
NPY_ARRAY_MEMORY_CONTIGUOUS_RO | NPY_ARRAY_ALIGNED);
151+
152+
return pyArray;
153+
}
154+
else
155+
{
156+
return NumpyAllocator<MatType>::allocate(mat,nd,shape);
157+
}
158+
}
108159
};
109160

110161
#endif

unittest/eigen_ref.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@ void fill(Eigen::Ref<MatType> mat, const typename MatType::Scalar & value)
3737
mat.fill(value);
3838
}
3939

40+
template<typename MatType>
41+
Eigen::Ref<MatType> asRef(const int rows, const int cols)
42+
{
43+
static MatType mat(rows,cols);
44+
std::cout << "mat:\n" << mat << std::endl;
45+
return mat;
46+
}
47+
4048
BOOST_PYTHON_MODULE(eigen_ref)
4149
{
4250
namespace bp = boost::python;
@@ -56,4 +64,6 @@ BOOST_PYTHON_MODULE(eigen_ref)
5664
bp::def("fillVec3", fill<Vector3d>);
5765
bp::def("fillVec", fill<VectorXd>);
5866
bp::def("fill", fill<MatrixXd>);
67+
68+
bp::def("asRef", asRef<MatrixXd>);
5969
}

unittest/matrix.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ MatrixDerived base(const Eigen::MatrixBase<MatrixDerived> & m)
8181
return m.derived();
8282
}
8383

84+
template<typename MatrixDerived>
85+
MatrixDerived plain(const Eigen::PlainObjectBase<MatrixDerived> & m)
86+
{
87+
return m.derived();
88+
}
89+
8490
template<typename Scalar>
8591
Eigen::Matrix<Scalar,6,6> matrix6(const Scalar & value)
8692
{
@@ -123,6 +129,9 @@ BOOST_PYTHON_MODULE(matrix)
123129

124130
bp::def("base", base<VectorXd>);
125131
bp::def("base", base<MatrixXd>);
132+
133+
bp::def("plain", plain<VectorXd>);
134+
bp::def("plain", plain<MatrixXd>);
126135

127136
bp::def("matrix6", matrix6<double>);
128137
}

unittest/python/test_eigen_ref.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,16 @@ def test(mat):
88
printMatrix(mat)
99
assert np.array_equal(mat,np.full(mat.shape,1.))
1010

11+
A_ref = asRef(mat.shape[0],mat.shape[1])
12+
A_ref.fill(1.)
13+
A_ref2 = asRef(mat.shape[0],mat.shape[1])
14+
15+
assert np.array_equal(A_ref,A_ref2)
16+
17+
A_ref2.fill(0)
18+
assert np.array_equal(A_ref,A_ref2)
19+
20+
1121
rows = 10
1222
cols = 30
1323

unittest/python/test_matrix.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@
3636
Mref_from_base = eigenpy.base(Mref)
3737
assert( np.array_equal(Mref,Mref_from_base) );
3838

39+
# Test plain function
40+
Mref_from_plain = eigenpy.plain(Mref)
41+
assert( np.array_equal(Mref,Mref_from_plain) );
42+
3943
if verbose: print("===> Matrix 8x8")
4044
M = Mref
4145
assert( np.array_equal(M,eigenpy.reflex(M,verbose)) );

0 commit comments

Comments
 (0)