Repository: stack-of-tasks/eigenpy (language: python)

Issue:
EigenPy seems to return wrong values for RowMajor matrices with 1 row
This error can be reproduced in Crocoddyl with the double-pendulum example: https://github.com/loco-3d/crocoddyl/blob/master/examples/double_pendulum.py

When I return the feedback gains (using `solver.K`), I observe that the first value reported by the bindings is the last column of the K matrix, e.g
```python
# Here code of the Crocoddyl example

# The code below returns array([-9.06294536e-02,  3.71715372e-05,  7.70249597e-05,  4.56026493e-05]),
# but the real value is [ 1.51564e-01 -7.09909e-01 -4.30023e-02 -9.06295e-02]
# (value obtained using std::cout in the c++ code)
print(solver.k[0])
```



Discussion / hints:
**Update**:
The above issue doesn't occur when we switch to NumPy matrices.
Could you isolate a simple reproducible example?
@jcarpent -- I have prepared a minimal example that depends on eigenpy only.
In the zip file, you have a Makefile that you need to adapt.
Please read the "readme" file for more instructions.

[eigenpy_bug.zip](https://github.com/stack-of-tasks/eigenpy/files/7982809/eigenpy_bug.zip)



Fix patch:
diff --git a/include/eigenpy/numpy-map.hpp b/include/eigenpy/numpy-map.hpp
index ed8d84629..8aba0944a 100644
--- a/include/eigenpy/numpy-map.hpp
+++ b/include/eigenpy/numpy-map.hpp
@@ -82,16 +82,32 @@ namespace eigenpy
           rows = (int)PyArray_DIMS(pyArray)[0];
           cols = 1;
           
-          inner_stride = (int)PyArray_STRIDE(pyArray, 0) / (int)itemsize;
-          outer_stride = 0;
+          if(EquivalentInputMatrixType::IsRowMajor)
+          {
+            outer_stride = (int)PyArray_STRIDE(pyArray, 0) / (int)itemsize;
+            inner_stride = 0;
+          }
+          else
+          {
+            inner_stride = (int)PyArray_STRIDE(pyArray, 0) / (int)itemsize;
+            outer_stride = 0;
+          }
         }
         else
         {
           rows = 1;
           cols = (int)PyArray_DIMS(pyArray)[0];
           
-          inner_stride = 0;
-          outer_stride = (int)PyArray_STRIDE(pyArray, 0) / (int)itemsize;
+          if(EquivalentInputMatrixType::IsRowMajor)
+          {
+            inner_stride = (int)PyArray_STRIDE(pyArray, 0) / (int)itemsize;
+            outer_stride = 0;
+          }
+          else
+          {
+            inner_stride = 0;
+            outer_stride = (int)PyArray_STRIDE(pyArray, 0) / (int)itemsize;
+          }
         }
       }
       


Test patch:
diff --git a/unittest/matrix.cpp b/unittest/matrix.cpp
index fd4ebae04..2982ee81f 100644
--- a/unittest/matrix.cpp
+++ b/unittest/matrix.cpp
@@ -1,6 +1,5 @@
 /*
- * Copyright 2014-2019, CNRS
- * Copyright 2018-2020, INRIA
+ * Copyright 2014-2022 CNRS INRIA
  */
 
 #include "eigenpy/eigenpy.hpp"
@@ -94,6 +93,61 @@ Eigen::Matrix<Scalar,6,6> matrix6(const Scalar & value)
   return ReturnType::Constant(value);
 }
 
+template<typename Scalar>
+Eigen::Matrix<Scalar,Eigen::Dynamic,Eigen::Dynamic,Eigen::RowMajor>
+generateRowMajorMatrix(const Eigen::DenseIndex rows,
+                       const Eigen::DenseIndex cols)
+{
+  typedef Eigen::Matrix<Scalar,Eigen::Dynamic,Eigen::Dynamic,Eigen::RowMajor> RowMajorMatrix;
+  RowMajorMatrix A(rows, cols);
+  typedef Eigen::Matrix<Scalar,Eigen::Dynamic,1> Vector;
+  Eigen::Map<Vector>(A.data(),A.size()) = Vector::LinSpaced(A.size(),1,A.size());
+  std::cout << "Matrix values:\n" << A << std::endl;
+  return A;
+}
+
+template<typename Scalar>
+Eigen::Matrix<Scalar,1,Eigen::Dynamic,Eigen::RowMajor>
+generateRowMajorVector(const Eigen::DenseIndex size)
+{
+  typedef Eigen::Matrix<Scalar,1,Eigen::Dynamic,Eigen::RowMajor> RowMajorVector;
+  RowMajorVector A(size);
+  A.setLinSpaced(size,1,size);
+  std::cout << "Vector values: " << A.transpose() << std::endl;
+  return A;
+}
+
+template<typename Scalar>
+Eigen::Matrix<Scalar,Eigen::Dynamic,Eigen::Dynamic>
+generateColMajorMatrix(const Eigen::DenseIndex rows,
+                       const Eigen::DenseIndex cols)
+{
+  typedef Eigen::Matrix<Scalar,Eigen::Dynamic,Eigen::Dynamic> ColMajorMatrix;
+  ColMajorMatrix A(rows, cols);
+  typedef Eigen::Matrix<Scalar,Eigen::Dynamic,1> Vector;
+  Eigen::Map<Vector>(A.data(),A.size()) = Vector::LinSpaced(A.size(),1,A.size());
+  std::cout << "Matrix values:\n" << A << std::endl;
+  return A;
+}
+
+template<typename Scalar>
+Eigen::Matrix<Scalar,1,Eigen::Dynamic>
+generateColMajorVector(const Eigen::DenseIndex size)
+{
+  typedef Eigen::Matrix<Scalar,1,Eigen::Dynamic> ColMajorVector;
+  ColMajorVector A(size);
+  A.setLinSpaced(size,1,size);
+  std::cout << "Vector values: " << A.transpose() << std::endl;
+  return A;
+}
+
+template<typename Matrix, typename ReturnMatrix>
+ReturnMatrix
+copy(const Eigen::MatrixBase<Matrix> & mat)
+{
+  return mat;
+}
+
 BOOST_PYTHON_MODULE(matrix)
 {
   using namespace Eigen;
@@ -134,4 +188,16 @@ BOOST_PYTHON_MODULE(matrix)
   bp::def("plain", plain<MatrixXd>);
 
   bp::def("matrix6", matrix6<double>);
+  
+  bp::def("generateRowMajorMatrix", generateRowMajorMatrix<double>);
+  bp::def("generateRowMajorVector", generateRowMajorVector<double>);
+  
+  bp::def("generateColMajorMatrix", generateColMajorMatrix<double>);
+  bp::def("generateColMajorVector", generateColMajorVector<double>);
+  
+  typedef Eigen::Matrix<double,Eigen::Dynamic,Eigen::Dynamic,Eigen::RowMajor> RowMajorMatrixXd;
+  bp::def("asRowMajorFromColMajorMatrix", copy<Eigen::MatrixXd,RowMajorMatrixXd>);
+  bp::def("asRowMajorFromColMajorVector", copy<Eigen::VectorXd,Eigen::RowVectorXd>);
+  bp::def("asRowMajorFromRowMajorMatrix", copy<RowMajorMatrixXd,RowMajorMatrixXd>);
+  bp::def("asRowMajorFromRowMajorVector", copy<Eigen::RowVectorXd,Eigen::RowVectorXd>);
 }
diff --git a/unittest/python/test_matrix.py b/unittest/python/test_matrix.py
index 1b1ea6e73..33b4c022b 100644
--- a/unittest/python/test_matrix.py
+++ b/unittest/python/test_matrix.py
@@ -134,3 +134,15 @@
 # test registration of matrix6
 mat6 = eigenpy.matrix6(0.)
 assert(mat6.size == 36)
+
+# test RowMajor
+
+mat = np.arange(0,10).reshape(2,5)
+assert((eigenpy.asRowMajorFromColMajorMatrix(mat) == mat).all())
+assert((eigenpy.asRowMajorFromRowMajorMatrix(mat) == mat).all())
+
+vec = np.arange(0,10)
+assert((eigenpy.asRowMajorFromColMajorMatrix(vec) == vec).all())
+assert((eigenpy.asRowMajorFromColMajorVector(vec) == vec).all())
+assert((eigenpy.asRowMajorFromRowMajorMatrix(vec) == vec).all())
+assert((eigenpy.asRowMajorFromRowMajorVector(vec) == vec).all())