From 115ed938e22e38b15936e30dc127a0f0bc1ec168 Mon Sep 17 00:00:00 2001
From: Justin Carpentier <justin.carpentier@inria.fr>
Date: Mon, 24 Aug 2020 19:39:57 +0200
Subject: [PATCH] core: add support of Eigen::Ref to Python

---
 include/eigenpy/details.hpp         |  6 +++
 include/eigenpy/eigen-to-python.hpp | 30 ++++++++++++++
 include/eigenpy/numpy-allocator.hpp | 61 ++++++++++++++++++++++++++---
 3 files changed, 92 insertions(+), 5 deletions(-)

diff --git a/include/eigenpy/details.hpp b/include/eigenpy/details.hpp
index f431c91d..dadfe458 100644
--- a/include/eigenpy/details.hpp
+++ b/include/eigenpy/details.hpp
@@ -79,7 +79,13 @@ namespace eigenpy
   {
     if(check_registration<MatType>()) return;
     
+    // to-python
     EigenToPyConverter<MatType>::registration();
+#if EIGEN_VERSION_AT_LEAST(3,2,0)
+    EigenToPyConverter< Eigen::Ref<MatType> >::registration();
+#endif
+    
+    // from-python
     EigenFromPyConverter<MatType>::registration();
   }
 
diff --git a/include/eigenpy/eigen-to-python.hpp b/include/eigenpy/eigen-to-python.hpp
index fac25371..bd89fec5 100644
--- a/include/eigenpy/eigen-to-python.hpp
+++ b/include/eigenpy/eigen-to-python.hpp
@@ -82,6 +82,36 @@ namespace eigenpy
     }
   };
 
+  template<typename MatType, int Options, typename Stride>
+  struct EigenToPy< Eigen::Ref<MatType,Options,Stride> >
+  {
+    static PyObject* convert(const Eigen::Ref<MatType,Options,Stride> & mat)
+    {
+      typedef Eigen::Ref<MatType,Options,Stride> EigenRef;
+      
+      assert( (mat.rows()<INT_MAX) && (mat.cols()<INT_MAX)
+             && "Matrix range larger than int ... should never happen." );
+      const npy_intp R = (npy_intp)mat.rows(), C = (npy_intp)mat.cols();
+      
+      PyArrayObject* pyArray;
+      // Allocate Python memory
+      if( ( ((!(C == 1) != !(R == 1)) && !MatType::IsVectorAtCompileTime) || MatType::IsVectorAtCompileTime)
+         && NumpyType::getType() == ARRAY_TYPE) // Handle array with a single dimension
+      {
+        npy_intp shape[1] = { C == 1 ? R : C };
+        pyArray = NumpyAllocator<EigenRef>::allocate(const_cast<EigenRef &>(mat),1,shape);
+      }
+      else
+      {
+        npy_intp shape[2] = { R,C };
+        pyArray = NumpyAllocator<EigenRef>::allocate(const_cast<EigenRef &>(mat),2,shape);
+      }
+      
+      // Create an instance (either np.array or np.matrix)
+      return NumpyType::make(pyArray).ptr();
+    }
+  };
+
   template<typename MatType>
   struct EigenToPyConverter
   {
diff --git a/include/eigenpy/numpy-allocator.hpp b/include/eigenpy/numpy-allocator.hpp
index a627c8ab..9cbeb8a0 100644
--- a/include/eigenpy/numpy-allocator.hpp
+++ b/include/eigenpy/numpy-allocator.hpp
@@ -57,7 +57,7 @@ namespace eigenpy
       }
       else
       {
-        return NumpyAllocator<MatType>::allocate(mat.derived(),nd,shape);
+        return NumpyAllocator<MatType>::allocate(mat,nd,shape);
       }
     }
   };
@@ -65,8 +65,33 @@ namespace eigenpy
 #if EIGEN_VERSION_AT_LEAST(3,2,0)
 
   template<typename MatType, int Options, typename Stride>
-  struct NumpyAllocator<Eigen::Ref<MatType,Options,Stride> > : NumpyAllocator<MatType &>
+  struct NumpyAllocator<Eigen::Ref<MatType,Options,Stride> >
   {
+    typedef Eigen::Ref<MatType,Options,Stride> RefType;
+    
+    static PyArrayObject * allocate(RefType & mat,
+                                    npy_intp nd, npy_intp * shape)
+    {
+      typedef typename RefType::Scalar Scalar;
+      enum { NPY_ARRAY_MEMORY_CONTIGUOUS = RefType::IsRowMajor ? NPY_ARRAY_CARRAY : NPY_ARRAY_FARRAY };
+      
+      if(NumpyType::sharedMemory())
+      {
+        const int Scalar_type_code = Register::getTypeCode<Scalar>();
+        PyArrayObject * pyArray = (PyArrayObject*) call_PyArray_New(getPyArrayType(),
+                                                                    static_cast<int>(nd),
+                                                                    shape,
+                                                                    Scalar_type_code,
+                                                                    mat.data(),
+                                                                    NPY_ARRAY_MEMORY_CONTIGUOUS | NPY_ARRAY_ALIGNED);
+        
+        return pyArray;
+      }
+      else
+      {
+        return NumpyAllocator<MatType>::allocate(mat,nd,shape);
+      }
+    }
   };
 
 #endif
@@ -88,14 +113,14 @@ namespace eigenpy
                                                                     static_cast<int>(nd),
                                                                     shape,
                                                                     Scalar_type_code,
-                                                                    const_cast<SimilarMatrixType &>(mat.derived()).data(),
+                                                                    const_cast<Scalar *>(mat.data()),
                                                                     NPY_ARRAY_MEMORY_CONTIGUOUS_RO | NPY_ARRAY_ALIGNED);
                                                                     
         return pyArray;
       }
       else
       {
-        return NumpyAllocator<MatType>::allocate(mat.derived(),nd,shape);
+        return NumpyAllocator<MatType>::allocate(mat,nd,shape);
       }
     }
   };
@@ -103,8 +128,34 @@ namespace eigenpy
 #if EIGEN_VERSION_AT_LEAST(3,2,0)
 
   template<typename MatType, int Options, typename Stride>
-  struct NumpyAllocator<const Eigen::Ref<const MatType,Options,Stride> > : NumpyAllocator<const MatType &>
+  struct NumpyAllocator<const Eigen::Ref<const MatType,Options,Stride> >
   {
+    typedef const Eigen::Ref<const MatType,Options,Stride> RefType;
+    
+    template<typename SimilarMatrixType>
+    static PyArrayObject * allocate(RefType & mat,
+                                    npy_intp nd, npy_intp * shape)
+    {
+      typedef typename SimilarMatrixType::Scalar Scalar;
+      enum { NPY_ARRAY_MEMORY_CONTIGUOUS_RO = SimilarMatrixType::IsRowMajor ? NPY_ARRAY_CARRAY_RO : NPY_ARRAY_FARRAY_RO };
+      
+      if(NumpyType::sharedMemory())
+      {
+        const int Scalar_type_code = Register::getTypeCode<Scalar>();
+        PyArrayObject * pyArray = (PyArrayObject*) call_PyArray_New(getPyArrayType(),
+                                                                    static_cast<int>(nd),
+                                                                    shape,
+                                                                    Scalar_type_code,
+                                                                    const_cast<Scalar *>(mat.data()),
+                                                                    NPY_ARRAY_MEMORY_CONTIGUOUS_RO | NPY_ARRAY_ALIGNED);
+                                                                    
+        return pyArray;
+      }
+      else
+      {
+        return NumpyAllocator<MatType>::allocate(mat,nd,shape);
+      }
+    }
   };
 
 #endif
-- 
GitLab