eigenpy 3.12.0
Bindings between Numpy and Eigen using Boost.Python
Loading...
Searching...
No Matches
eigen-from-python.hpp
1//
2// Copyright (c) 2014-2023 CNRS INRIA
3//
4
5#ifndef __eigenpy_eigen_from_python_hpp__
6#define __eigenpy_eigen_from_python_hpp__
7
8#include "eigenpy/fwd.hpp"
9#include "eigenpy/eigen-allocator.hpp"
10#include "eigenpy/numpy-type.hpp"
11#include "eigenpy/scalar-conversion.hpp"
12
13namespace eigenpy {
14
15template <typename EigenType,
16 typename BaseType = typename get_eigen_base_type<EigenType>::type>
18
19template <typename MatType>
20struct expected_pytype_for_arg<MatType, Eigen::MatrixBase<MatType>> {
21 static PyTypeObject const *get_pytype() {
22 PyTypeObject const *py_type = eigenpy::getPyArrayType();
23 return py_type;
24 }
25};
26
27} // namespace eigenpy
28
29namespace boost {
30namespace python {
31namespace converter {
32
33template <typename Scalar, int Rows, int Cols, int Options, int MaxRows,
34 int MaxCols>
35struct expected_pytype_for_arg<
36 Eigen::Matrix<Scalar, Rows, Cols, Options, MaxRows, MaxCols>>
38 Eigen::Matrix<Scalar, Rows, Cols, Options, MaxRows, MaxCols>> {};
39
40} // namespace converter
41} // namespace python
42} // namespace boost
43
44namespace eigenpy {
45namespace details {
46template <typename MatType, bool is_const = boost::is_const<MatType>::value>
48 static void run(const Eigen::MatrixBase<MatType> &input,
49 PyArrayObject *pyArray) {
50 EigenAllocator<MatType>::copy(input, pyArray);
51 }
52};
53
54template <typename MatType>
55struct copy_if_non_const<const MatType, true> {
56 static void run(const Eigen::MatrixBase<MatType> & /*input*/,
57 PyArrayObject * /*pyArray*/) {}
58};
59
60#if EIGEN_VERSION_AT_LEAST(3, 2, 0)
61
62template <typename _RefType>
63struct referent_storage_eigen_ref {
64 typedef _RefType RefType;
65 typedef typename get_eigen_plain_type<RefType>::type PlainObjectType;
66 typedef typename ::eigenpy::aligned_storage<
67 ::boost::python::detail::referent_size<RefType &>::value>::type
68 AlignedStorage;
69
70 referent_storage_eigen_ref()
71 : pyArray(NULL),
72 plain_ptr(NULL),
73 ref_ptr(reinterpret_cast<RefType *>(ref_storage.bytes)) {}
74
75 referent_storage_eigen_ref(const RefType &ref, PyArrayObject *pyArray,
76 PlainObjectType *plain_ptr = NULL)
77 : pyArray(pyArray),
78 plain_ptr(plain_ptr),
79 ref_ptr(reinterpret_cast<RefType *>(ref_storage.bytes)) {
80 Py_INCREF(pyArray);
81 new (ref_storage.bytes) RefType(ref);
82 }
83
84 ~referent_storage_eigen_ref() {
85 if (plain_ptr != NULL && PyArray_ISWRITEABLE(pyArray))
86 copy_if_non_const<PlainObjectType>::run(*plain_ptr, pyArray);
87
88 Py_DECREF(pyArray);
89
90 if (plain_ptr != NULL) plain_ptr->~PlainObjectType();
91
92 ref_ptr->~RefType();
93 }
94
95 AlignedStorage ref_storage;
96 PyArrayObject *pyArray;
97 PlainObjectType *plain_ptr;
98 RefType *ref_ptr;
99};
100#endif
101
102} // namespace details
103} // namespace eigenpy
104
105namespace boost {
106namespace python {
107namespace detail {
108#if EIGEN_VERSION_AT_LEAST(3, 2, 0)
109template <typename MatType, int Options, typename Stride>
110struct referent_storage<Eigen::Ref<MatType, Options, Stride> &> {
111 typedef Eigen::Ref<MatType, Options, Stride> RefType;
112 typedef ::eigenpy::details::referent_storage_eigen_ref<RefType> StorageType;
113 typedef typename ::eigenpy::aligned_storage<
114 referent_size<StorageType &>::value>::type type;
115};
116
117template <typename MatType, int Options, typename Stride>
118struct referent_storage<const Eigen::Ref<const MatType, Options, Stride> &> {
119 typedef Eigen::Ref<const MatType, Options, Stride> RefType;
120 typedef ::eigenpy::details::referent_storage_eigen_ref<RefType> StorageType;
121 typedef typename ::eigenpy::aligned_storage<
122 referent_size<StorageType &>::value>::type type;
123};
124#endif
125} // namespace detail
126} // namespace python
127} // namespace boost
128
129namespace boost {
130namespace python {
131namespace converter {
132
133#define EIGENPY_RVALUE_FROM_PYTHON_DATA_INIT(type) \
134 typedef ::eigenpy::rvalue_from_python_data<type> Base; \
135 \
136 rvalue_from_python_data(rvalue_from_python_stage1_data const &_stage1) \
137 : Base(_stage1) {} \
138 \
139 rvalue_from_python_data(void *convertible) : Base(convertible){};
140
141template <typename Scalar, int Rows, int Cols, int Options, int MaxRows,
142 int MaxCols>
143struct rvalue_from_python_data<
144 Eigen::Matrix<Scalar, Rows, Cols, Options, MaxRows, MaxCols> const &>
146 Scalar, Rows, Cols, Options, MaxRows, MaxCols> const &> {
147 typedef Eigen::Matrix<Scalar, Rows, Cols, Options, MaxRows, MaxCols> T;
148 EIGENPY_RVALUE_FROM_PYTHON_DATA_INIT(T const &)
149};
150
151template <typename Derived>
152struct rvalue_from_python_data<Eigen::MatrixBase<Derived> const &>
153 : ::eigenpy::rvalue_from_python_data<Derived const &> {
154 EIGENPY_RVALUE_FROM_PYTHON_DATA_INIT(Derived const &)
155};
156
157template <typename Derived>
158struct rvalue_from_python_data<Eigen::EigenBase<Derived> const &>
159 : ::eigenpy::rvalue_from_python_data<Derived const &> {
160 EIGENPY_RVALUE_FROM_PYTHON_DATA_INIT(Derived const &)
161};
162
163template <typename Derived>
164struct rvalue_from_python_data<Eigen::PlainObjectBase<Derived> const &>
165 : ::eigenpy::rvalue_from_python_data<Derived const &> {
166 EIGENPY_RVALUE_FROM_PYTHON_DATA_INIT(Derived const &)
167};
168
169template <typename MatType, int Options, typename Stride>
170struct rvalue_from_python_data<Eigen::Ref<MatType, Options, Stride> &>
171 : rvalue_from_python_storage<Eigen::Ref<MatType, Options, Stride> &> {
172 typedef Eigen::Ref<MatType, Options, Stride> RefType;
173
174#if (!defined(__MWERKS__) || __MWERKS__ >= 0x3000) && \
175 (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 245) && \
176 (!defined(__DECCXX_VER) || __DECCXX_VER > 60590014) && \
177 !defined(BOOST_PYTHON_SYNOPSIS) /* Synopsis' OpenCXX has trouble parsing \
178 this */
179 // This must always be a POD struct with m_data its first member.
180 BOOST_STATIC_ASSERT(BOOST_PYTHON_OFFSETOF(rvalue_from_python_storage<RefType>,
181 stage1) == 0);
182#endif
183
184 // The usual constructor
185 rvalue_from_python_data(rvalue_from_python_stage1_data const &_stage1) {
186 this->stage1 = _stage1;
187 }
188
189 // This constructor just sets m_convertible -- used by
190 // implicitly_convertible<> to perform the final step of the
191 // conversion, where the construct() function is already known.
192 rvalue_from_python_data(void *convertible) {
193 this->stage1.convertible = convertible;
194 }
195
196 // Destroys any object constructed in the storage.
197 ~rvalue_from_python_data() {
198 typedef ::eigenpy::details::referent_storage_eigen_ref<RefType> StorageType;
199 if (this->stage1.convertible == this->storage.bytes)
200 static_cast<StorageType *>((void *)this->storage.bytes)->~StorageType();
201 }
202};
203
204template <typename MatType, int Options, typename Stride>
205struct rvalue_from_python_data<
206 const Eigen::Ref<const MatType, Options, Stride> &>
207 : rvalue_from_python_storage<
208 const Eigen::Ref<const MatType, Options, Stride> &> {
209 typedef Eigen::Ref<const MatType, Options, Stride> RefType;
210
211#if (!defined(__MWERKS__) || __MWERKS__ >= 0x3000) && \
212 (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 245) && \
213 (!defined(__DECCXX_VER) || __DECCXX_VER > 60590014) && \
214 !defined(BOOST_PYTHON_SYNOPSIS) /* Synopsis' OpenCXX has trouble parsing \
215 this */
216 // This must always be a POD struct with m_data its first member.
217 BOOST_STATIC_ASSERT(BOOST_PYTHON_OFFSETOF(rvalue_from_python_storage<RefType>,
218 stage1) == 0);
219#endif
220
221 // The usual constructor
222 rvalue_from_python_data(rvalue_from_python_stage1_data const &_stage1) {
223 this->stage1 = _stage1;
224 }
225
226 // This constructor just sets m_convertible -- used by
227 // implicitly_convertible<> to perform the final step of the
228 // conversion, where the construct() function is already known.
229 rvalue_from_python_data(void *convertible) {
230 this->stage1.convertible = convertible;
231 }
232
233 // Destroys any object constructed in the storage.
234 ~rvalue_from_python_data() {
235 typedef ::eigenpy::details::referent_storage_eigen_ref<RefType> StorageType;
236 if (this->stage1.convertible == this->storage.bytes)
237 static_cast<StorageType *>((void *)this->storage.bytes)->~StorageType();
238 }
239};
240
241} // namespace converter
242} // namespace python
243} // namespace boost
244
245namespace eigenpy {
246
247template <typename MatOrRefType>
248void eigen_from_py_construct(
249 PyObject *pyObj, bp::converter::rvalue_from_python_stage1_data *memory) {
250 PyArrayObject *pyArray = reinterpret_cast<PyArrayObject *>(pyObj);
251 assert((PyArray_DIMS(pyArray)[0] < INT_MAX) &&
252 (PyArray_DIMS(pyArray)[1] < INT_MAX));
253
254 bp::converter::rvalue_from_python_storage<MatOrRefType> *storage =
255 reinterpret_cast<
256 bp::converter::rvalue_from_python_storage<MatOrRefType> *>(
257 reinterpret_cast<void *>(memory));
258
259 EigenAllocator<MatOrRefType>::allocate(pyArray, storage);
260
261 memory->convertible = storage->storage.bytes;
262}
263
264template <typename EigenType,
265 typename BaseType = typename get_eigen_base_type<EigenType>::type>
267 typedef typename EigenType::Scalar Scalar;
268
270 static void *convertible(PyObject *pyObj);
271
273 static void construct(PyObject *pyObj,
274 bp::converter::rvalue_from_python_stage1_data *memory);
275
276 static void registration();
277};
278
279template <typename MatType>
280struct eigen_from_py_impl<MatType, Eigen::MatrixBase<MatType>> {
281 typedef typename MatType::Scalar Scalar;
282
284 static void *convertible(PyObject *pyObj);
285
287 static void construct(PyObject *pyObj,
288 bp::converter::rvalue_from_python_stage1_data *memory);
289
290 static void registration();
291};
292
293template <typename EigenType,
294 typename Scalar =
295 typename boost::remove_reference<EigenType>::type::Scalar>
296struct EigenFromPy : eigen_from_py_impl<EigenType> {};
297
298template <typename MatType>
300 PyObject *pyObj) {
301 if (!call_PyArray_Check(reinterpret_cast<PyObject *>(pyObj))) return 0;
302
303 PyArrayObject *pyArray = reinterpret_cast<PyArrayObject *>(pyObj);
304
305 if (!np_type_is_convertible_into_scalar<Scalar>(
306 EIGENPY_GET_PY_ARRAY_TYPE(pyArray)))
307 return 0;
308
309 if (MatType::IsVectorAtCompileTime) {
310 const Eigen::DenseIndex size_at_compile_time =
311 MatType::IsRowMajor ? MatType::ColsAtCompileTime
312 : MatType::RowsAtCompileTime;
313
314 switch (PyArray_NDIM(pyArray)) {
315 case 0:
316 return 0;
317 case 1: {
318 if (size_at_compile_time != Eigen::Dynamic) {
319 // check that the sizes at compile time matche
320 if (PyArray_DIMS(pyArray)[0] == size_at_compile_time)
321 return pyArray;
322 else
323 return 0;
324 } else // This is a dynamic MatType
325 return pyArray;
326 }
327 case 2: {
328 // Special care of scalar matrix of dimension 1x1.
329 if (PyArray_DIMS(pyArray)[0] == 1 && PyArray_DIMS(pyArray)[1] == 1) {
330 if (size_at_compile_time != Eigen::Dynamic) {
331 if (size_at_compile_time == 1)
332 return pyArray;
333 else
334 return 0;
335 } else // This is a dynamic MatType
336 return pyArray;
337 }
338
339 if (PyArray_DIMS(pyArray)[0] > 1 && PyArray_DIMS(pyArray)[1] > 1) {
340 return 0;
341 }
342
343 if (((PyArray_DIMS(pyArray)[0] == 1) &&
344 (MatType::ColsAtCompileTime == 1)) ||
345 ((PyArray_DIMS(pyArray)[1] == 1) &&
346 (MatType::RowsAtCompileTime == 1))) {
347 return 0;
348 }
349
350 if (size_at_compile_time !=
351 Eigen::Dynamic) { // This is a fixe size vector
352 const Eigen::DenseIndex pyArray_size =
353 PyArray_DIMS(pyArray)[0] > PyArray_DIMS(pyArray)[1]
354 ? PyArray_DIMS(pyArray)[0]
355 : PyArray_DIMS(pyArray)[1];
356 if (size_at_compile_time != pyArray_size) return 0;
357 }
358 break;
359 }
360 default:
361 return 0;
362 }
363 } else // this is a matrix
364 {
365 if (PyArray_NDIM(pyArray) ==
366 1) // We can always convert a vector into a matrix
367 {
368 return pyArray;
369 }
370
371 if (PyArray_NDIM(pyArray) != 2) {
372 return 0;
373 }
374
375 if (PyArray_NDIM(pyArray) == 2) {
376 const int R = (int)PyArray_DIMS(pyArray)[0];
377 const int C = (int)PyArray_DIMS(pyArray)[1];
378
379 if ((MatType::RowsAtCompileTime != R) &&
380 (MatType::RowsAtCompileTime != Eigen::Dynamic))
381 return 0;
382 if ((MatType::ColsAtCompileTime != C) &&
383 (MatType::ColsAtCompileTime != Eigen::Dynamic))
384 return 0;
385 }
386 }
387
388#ifdef NPY_1_8_API_VERSION
389 if (!(PyArray_FLAGS(pyArray)))
390#else
391 if (!(PyArray_FLAGS(pyArray) & NPY_ALIGNED))
392#endif
393 {
394 return 0;
395 }
396
397 return pyArray;
398}
399
400template <typename MatType>
402 PyObject *pyObj, bp::converter::rvalue_from_python_stage1_data *memory) {
403 eigen_from_py_construct<MatType>(pyObj, memory);
404}
405
406template <typename MatType>
408 bp::converter::registry::push_back(
409 reinterpret_cast<void *(*)(_object *)>(&eigen_from_py_impl::convertible),
410 &eigen_from_py_impl::construct, bp::type_id<MatType>()
411#ifndef BOOST_PYTHON_NO_PY_SIGNATURES
412 ,
414#endif
415 );
416}
417
418template <typename EigenType,
419 typename BaseType = typename get_eigen_base_type<EigenType>::type>
421
422template <typename EigenType>
424
425template <typename MatType>
426struct eigen_from_py_converter_impl<MatType, Eigen::MatrixBase<MatType>> {
427 static void registration() {
428 EigenFromPy<MatType>::registration();
429
430 // Add conversion to Eigen::MatrixBase<MatType>
431 typedef Eigen::MatrixBase<MatType> MatrixBase;
432 EigenFromPy<MatrixBase>::registration();
433
434 // Add conversion to Eigen::EigenBase<MatType>
435 typedef Eigen::EigenBase<MatType> EigenBase;
436 EigenFromPy<EigenBase, typename MatType::Scalar>::registration();
437
438 // Add conversion to Eigen::PlainObjectBase<MatType>
439 typedef Eigen::PlainObjectBase<MatType> PlainObjectBase;
440 EigenFromPy<PlainObjectBase>::registration();
441
442#if EIGEN_VERSION_AT_LEAST(3, 2, 0)
443 // Add conversion to Eigen::Ref<MatType>
444 typedef Eigen::Ref<MatType> RefType;
445 EigenFromPy<RefType>::registration();
446
447 // Add conversion to Eigen::Ref<MatType>
448 typedef const Eigen::Ref<const MatType> ConstRefType;
449 EigenFromPy<ConstRefType>::registration();
450#endif
451 }
452};
453
454template <typename MatType>
455struct EigenFromPy<Eigen::MatrixBase<MatType>> : EigenFromPy<MatType> {
456 typedef EigenFromPy<MatType> EigenFromPyDerived;
457 typedef Eigen::MatrixBase<MatType> Base;
458
459 static void registration() {
460 bp::converter::registry::push_back(
461 reinterpret_cast<void *(*)(_object *)>(&EigenFromPy::convertible),
462 &EigenFromPy::construct, bp::type_id<Base>()
463#ifndef BOOST_PYTHON_NO_PY_SIGNATURES
464 ,
466#endif
467 );
468 }
469};
470
471template <typename MatType>
472struct EigenFromPy<Eigen::EigenBase<MatType>, typename MatType::Scalar>
473 : EigenFromPy<MatType> {
474 typedef EigenFromPy<MatType> EigenFromPyDerived;
475 typedef Eigen::EigenBase<MatType> Base;
476
477 static void registration() {
478 bp::converter::registry::push_back(
479 reinterpret_cast<void *(*)(_object *)>(&EigenFromPy::convertible),
480 &EigenFromPy::construct, bp::type_id<Base>()
481#ifndef BOOST_PYTHON_NO_PY_SIGNATURES
482 ,
484#endif
485 );
486 }
487};
488
489template <typename MatType>
490struct EigenFromPy<Eigen::PlainObjectBase<MatType>> : EigenFromPy<MatType> {
491 typedef EigenFromPy<MatType> EigenFromPyDerived;
492 typedef Eigen::PlainObjectBase<MatType> Base;
493
494 static void registration() {
495 bp::converter::registry::push_back(
496 reinterpret_cast<void *(*)(_object *)>(&EigenFromPy::convertible),
497 &EigenFromPy::construct, bp::type_id<Base>()
498#ifndef BOOST_PYTHON_NO_PY_SIGNATURES
499 ,
501#endif
502 );
503 }
504};
505
506#if EIGEN_VERSION_AT_LEAST(3, 2, 0)
507
508template <typename MatType, int Options, typename Stride>
509struct EigenFromPy<Eigen::Ref<MatType, Options, Stride>> {
510 typedef Eigen::Ref<MatType, Options, Stride> RefType;
511 typedef typename MatType::Scalar Scalar;
512
514 static void *convertible(PyObject *pyObj) {
515 if (!call_PyArray_Check(pyObj)) return 0;
516 PyArrayObject *pyArray = reinterpret_cast<PyArrayObject *>(pyObj);
517 if (!PyArray_ISWRITEABLE(pyArray)) return 0;
519 }
520
521 static void registration() {
522 bp::converter::registry::push_back(
523 reinterpret_cast<void *(*)(_object *)>(&EigenFromPy::convertible),
524 &eigen_from_py_construct<RefType>, bp::type_id<RefType>()
525#ifndef BOOST_PYTHON_NO_PY_SIGNATURES
526 ,
528#endif
529 );
530 }
531};
532
533template <typename MatType, int Options, typename Stride>
534struct EigenFromPy<const Eigen::Ref<const MatType, Options, Stride>> {
535 typedef const Eigen::Ref<const MatType, Options, Stride> ConstRefType;
536 typedef typename MatType::Scalar Scalar;
537
539 static void *convertible(PyObject *pyObj) {
541 }
542
543 static void registration() {
544 bp::converter::registry::push_back(
545 reinterpret_cast<void *(*)(_object *)>(&EigenFromPy::convertible),
546 &eigen_from_py_construct<ConstRefType>, bp::type_id<ConstRefType>()
547#ifndef BOOST_PYTHON_NO_PY_SIGNATURES
548 ,
549 &eigenpy::expected_pytype_for_arg<MatType>::get_pytype
550#endif
551 );
552 }
553};
554#endif
555
556} // namespace eigenpy
557
558#ifdef EIGENPY_WITH_TENSOR_SUPPORT
559#include "eigenpy/tensor/eigen-from-python.hpp"
560#endif
561
562#include "eigenpy/sparse/eigen-from-python.hpp"
563
564#endif // __eigenpy_eigen_from_python_hpp__
static void * convertible(PyObject *pyObj)
Determine if pyObj can be converted into a MatType object.
static void construct(PyObject *pyObj, bp::converter::rvalue_from_python_stage1_data *memory)
Allocate memory and copy pyObj in the new storage.
static void construct(PyObject *pyObj, bp::converter::rvalue_from_python_stage1_data *memory)
Allocate memory and copy pyObj in the new storage.
static void * convertible(PyObject *pyObj)
Determine if pyObj can be converted into a MatType object.