eigen-from-python.hpp 16.7 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
//
// Copyright (c) 2014-2020 CNRS INRIA
//

#ifndef __eigenpy_eigen_from_python_hpp__
#define __eigenpy_eigen_from_python_hpp__

#include "eigenpy/fwd.hpp"
#include "eigenpy/numpy-type.hpp"
#include "eigenpy/eigen-allocator.hpp"
#include "eigenpy/scalar-conversion.hpp"

13
14
#include <boost/python/converter/rvalue_from_python_data.hpp>

15
16
17
18
namespace eigenpy
{
  namespace details
  {
19
    template<typename MatType, bool is_const = boost::is_const<MatType>::value>
20
21
22
23
24
25
26
27
28
29
    struct copy_if_non_const
    {
      static void run(const Eigen::MatrixBase<MatType> & input,
                      PyArrayObject * pyArray)
      {
        EigenAllocator<MatType>::copy(input,pyArray);
      }
    };
  
    template<typename MatType>
30
    struct copy_if_non_const<const MatType,true>
31
32
33
34
35
36
37
    {
      static void run(const Eigen::MatrixBase<MatType> & /*input*/,
                      PyArrayObject * /*pyArray*/)
      {}
    };
  
#if EIGEN_VERSION_AT_LEAST(3,2,0)
38
    template<typename MatType, int Options, typename Stride> struct referent_storage_eigen_ref;
39
  
40
    template<typename MatType, int Options, typename Stride>
41
42
    struct referent_storage_eigen_ref
    {
43
      typedef Eigen::Ref<MatType,Options,Stride> RefType;
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
      
      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)
92
93
  template<typename MatType, int Options, typename Stride>
  struct referent_storage<Eigen::Ref<MatType,Options,Stride> &>
94
  {
95
    typedef ::eigenpy::details::referent_storage_eigen_ref<MatType,Options,Stride> StorageType;
96
97
98
99
    typedef aligned_storage<
        ::boost::python::detail::referent_size<StorageType&>::value
    > type;
  };
100
101
102
103
104
105
106
107
108

  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;
  };
109
110
111
#endif
}}}

112
113
namespace boost { namespace python { namespace converter {

114
115
116
  template<typename MatrixReference>
  struct rvalue_from_python_data_eigen
  : rvalue_from_python_storage<MatrixReference>
117
  {
118
    typedef MatrixReference T;
119
120
121
122
123
124
125
126
127
128
    
# 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
129
    rvalue_from_python_data_eigen(rvalue_from_python_stage1_data const & _stage1)
130
131
132
133
134
135
136
    {
      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.
137
    rvalue_from_python_data_eigen(void* convertible)
138
139
140
141
142
    {
      this->stage1.convertible = convertible;
    }
    
    // Destroys any object constructed in the storage.
143
    ~rvalue_from_python_data_eigen()
144
    {
145
      typedef typename boost::remove_const<typename boost::remove_reference<MatrixReference>::type>::type MatrixType;
146
      if (this->stage1.convertible == this->storage.bytes)
147
        static_cast<MatrixType *>((void *)this->storage.bytes)->~MatrixType();
148
149
150
    }
  };

151
152
153
154
155
156
157
158
159
#define RVALUE_FROM_PYTHON_DATA_INIT(type)                                 \
  typedef rvalue_from_python_data_eigen<type> Base;                        \
                                                                           \
  rvalue_from_python_data(rvalue_from_python_stage1_data const & _stage1)  \
  : Base(_stage1)                                                          \
  {}                                                                       \
                                                                           \
  rvalue_from_python_data(void* convertible) : Base(convertible) {};

160
161
  /// \brief Template specialization of rvalue_from_python_data
  template<typename Derived>
162
163
  struct rvalue_from_python_data<Eigen::MatrixBase<Derived> const &>
  : rvalue_from_python_data_eigen<Derived const &>
164
  {
165
    RVALUE_FROM_PYTHON_DATA_INIT(Derived const &)
166
167
  };

168
169
170
171
172
173
174
175
176
177
  /// \brief Template specialization of rvalue_from_python_data
  template<typename Derived>
  struct rvalue_from_python_data<Eigen::EigenBase<Derived> const &>
  : rvalue_from_python_data_eigen<Derived const &>
  {
    RVALUE_FROM_PYTHON_DATA_INIT(Derived const &)
  };

#undef RVALUE_FROM_PYTHON_DATA_INIT

178
  template<typename MatType, int Options, typename Stride>
179
180
  struct rvalue_from_python_data<Eigen::Ref<MatType,Options,Stride> &>
  : rvalue_from_python_storage<Eigen::Ref<MatType,Options,Stride> &>
181
  {
182
    typedef Eigen::Ref<MatType,Options,Stride> T;
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208

# 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()
    {
209
      typedef ::eigenpy::details::referent_storage_eigen_ref<MatType, Options,Stride> StorageType;
210
211
212
213
214
      if (this->stage1.convertible == this->storage.bytes)
        static_cast<StorageType *>((void *)this->storage.bytes)->~StorageType();
    }
  };

215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
  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();
    }
  };

252
253
} } }

254
255
namespace eigenpy
{
256
257
258
259
260
261
262
263
264
265
266
267

  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);
Justin Carpentier's avatar
Justin Carpentier committed
268

269
270
271
    memory->convertible = storage->storage.bytes;
  }

272
273
274
  template<typename MatType>
  struct EigenFromPy
  {
275
    typedef typename MatType::Scalar Scalar;
276
277
    
    /// \brief Determine if pyObj can be converted into a MatType object
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
    static void* convertible(PyArrayObject* pyArray);
 
    /// \brief Allocate memory and copy pyObj in the new storage
    static void construct(PyObject* pyObj,
                          bp::converter::rvalue_from_python_stage1_data* memory);
    
    static void registration();
  };

  template<typename MatType>
  void* EigenFromPy<MatType>::convertible(PyArrayObject* pyArray)
  {
    if(!PyArray_Check(pyArray))
      return 0;
    
    if(!np_type_is_convertible_into_scalar<Scalar>(EIGENPY_GET_PY_ARRAY_TYPE(pyArray)))
      return 0;
    
    if(MatType::IsVectorAtCompileTime)
297
    {
298
299
300
301
      const Eigen::DenseIndex size_at_compile_time
      = MatType::IsRowMajor
      ? MatType::ColsAtCompileTime
      : MatType::RowsAtCompileTime;
302
      
303
      switch(PyArray_NDIM(pyArray))
304
      {
305
306
307
        case 0:
          return 0;
        case 1:
308
        {
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
          if(size_at_compile_time != Eigen::Dynamic)
          {
            // check that the sizes at compile time matche
            if(PyArray_DIMS(pyArray)[0] == size_at_compile_time)
              return pyArray;
            else
              return 0;
          }
          else // This is a dynamic MatType
            return pyArray;
        }
        case 2:
        {
          // Special care of scalar matrix of dimension 1x1.
          if(PyArray_DIMS(pyArray)[0] == 1 && PyArray_DIMS(pyArray)[1] == 1)
324
325
326
          {
            if(size_at_compile_time != Eigen::Dynamic)
            {
327
              if(size_at_compile_time == 1)
328
329
330
331
332
333
334
                return pyArray;
              else
                return 0;
            }
            else // This is a dynamic MatType
              return pyArray;
          }
335
336
          
          if(PyArray_DIMS(pyArray)[0] > 1 && PyArray_DIMS(pyArray)[1] > 1)
337
          {
338
            return 0;
339
          }
340
341
342
343
          
          if(((PyArray_DIMS(pyArray)[0] == 1) && (MatType::ColsAtCompileTime == 1))
             || ((PyArray_DIMS(pyArray)[1] == 1) && (MatType::RowsAtCompileTime == 1)))
          {
344
            return 0;
345
346
347
348
349
350
351
352
353
354
355
356
          }
          
          if(size_at_compile_time != Eigen::Dynamic)
          { // This is a fixe size vector
            const Eigen::DenseIndex pyArray_size
            = PyArray_DIMS(pyArray)[0] > PyArray_DIMS(pyArray)[1]
            ? PyArray_DIMS(pyArray)[0]
            : PyArray_DIMS(pyArray)[1];
            if(size_at_compile_time != pyArray_size)
              return 0;
          }
          break;
357
        }
358
359
360
361
362
363
364
365
366
        default:
          return 0;
      }
    }
    else // this is a matrix
    {
      if(PyArray_NDIM(pyArray) == 1) // We can always convert a vector into a matrix
      {
        return pyArray;
367
      }
368
369
      
      if(PyArray_NDIM(pyArray) != 2)
370
      {
371
372
373
374
375
376
377
        return 0;
      }
      
      if(PyArray_NDIM(pyArray) == 2)
      {
        const int R = (int)PyArray_DIMS(pyArray)[0];
        const int C = (int)PyArray_DIMS(pyArray)[1];
378
        
379
380
381
382
383
        if( (MatType::RowsAtCompileTime!=R)
           && (MatType::RowsAtCompileTime!=Eigen::Dynamic) )
          return 0;
        if( (MatType::ColsAtCompileTime!=C)
           && (MatType::ColsAtCompileTime!=Eigen::Dynamic) )
384
385
          return 0;
      }
386
387
    }
    
388
#ifdef NPY_1_8_API_VERSION
389
    if(!(PyArray_FLAGS(pyArray)))
390
#else
391
    if(!(PyArray_FLAGS(pyArray) & NPY_ALIGNED))
392
393
#endif
    {
394
      return 0;
395
396
    }
    
397
398
399
400
401
402
403
    return pyArray;
  }

  template<typename MatType>
  void EigenFromPy<MatType>::construct(PyObject* pyObj,
                                       bp::converter::rvalue_from_python_stage1_data* memory)
  {
404
    eigen_from_py_construct<MatType>(pyObj,memory);
405
406
407
408
409
410
411
412
413
  }

  template<typename MatType>
  void EigenFromPy<MatType>::registration()
  {
    bp::converter::registry::push_back
    (reinterpret_cast<void *(*)(_object *)>(&EigenFromPy::convertible),
     &EigenFromPy::construct,bp::type_id<MatType>());
  }
414
415
416
417
418
419
420
421
  
  template<typename MatType>
  struct EigenFromPyConverter
  {
    static void registration()
    {
      EigenFromPy<MatType>::registration();

Justin Carpentier's avatar
Justin Carpentier committed
422
      // Add conversion to Eigen::MatrixBase<MatType>
423
424
425
      typedef Eigen::MatrixBase<MatType> MatrixBase;
      EigenFromPy<MatrixBase>::registration();

Justin Carpentier's avatar
Justin Carpentier committed
426
      // Add conversion to Eigen::EigenBase<MatType>
427
428
      typedef Eigen::EigenBase<MatType> EigenBase;
      EigenFromPy<EigenBase>::registration();
429
430
431
432
433

#if EIGEN_VERSION_AT_LEAST(3,2,0)
      // Add conversion to Eigen::Ref<MatType>
      typedef Eigen::Ref<MatType> RefType;
      EigenFromPy<RefType>::registration();
434
435
436
437
      
      // Add conversion to Eigen::Ref<MatType>
      typedef const Eigen::Ref<const MatType> ConstRefType;
      EigenFromPy<ConstRefType>::registration();
438
#endif
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
    }
  };

  template<typename MatType>
  struct EigenFromPy< Eigen::MatrixBase<MatType> > : EigenFromPy<MatType>
  {
    typedef EigenFromPy<MatType> EigenFromPyDerived;
    typedef Eigen::MatrixBase<MatType> Base;

    static void registration()
    {
      bp::converter::registry::push_back
      (reinterpret_cast<void *(*)(_object *)>(&EigenFromPy::convertible),
       &EigenFromPy::construct,bp::type_id<Base>());
    }
  };
    
  template<typename MatType>
  struct EigenFromPy< Eigen::EigenBase<MatType> > : EigenFromPy<MatType>
  {
    typedef EigenFromPy<MatType> EigenFromPyDerived;
    typedef Eigen::EigenBase<MatType> Base;

    static void registration()
    {
      bp::converter::registry::push_back
      (reinterpret_cast<void *(*)(_object *)>(&EigenFromPy::convertible),
       &EigenFromPy::construct,bp::type_id<Base>());
    }
  };

#if EIGEN_VERSION_AT_LEAST(3,2,0)
471

472
473
  template<typename MatType, int Options, typename Stride>
  struct EigenFromPy<Eigen::Ref<MatType,Options,Stride> >
474
  {
475
    typedef Eigen::Ref<MatType,Options,Stride> RefType;
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
    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>());
    }
  };

496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
  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>());
    }
  };

516
517
518
519
520
521
522
523
524
525
526
527
  // Template specialization for Eigen::Ref
  template<typename MatType>
  struct EigenFromPyConverter< eigenpy::Ref<MatType> >
  {
    static void registration()
    {
      bp::converter::registry::push_back
      (reinterpret_cast<void *(*)(_object *)>(&EigenFromPy<MatType>::convertible),
       &EigenFromPy<MatType>::construct,bp::type_id<MatType>());
    }
  };
#endif
528

529
530
531
}

#endif // __eigenpy_eigen_from_python_hpp__