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

Issue:
Quaternion's constructor does not recognize a 4-element numpy array
The following test:
```py
import numpy as np
import eigenpy as eig

v = np.array([0.5, 0.5, -0.5, 0.5])
q = eig.Quaternion(v)
```
fails with the following error:
```
Traceback (most recent call last):
  File "test_eigenpy.py", line 5, in <module>
    q = eig.Quaternion(v)
RuntimeError: The number of rows does not fit with the matrix type.
```

On the other hand, the slightly modified test below, using a two-dimensional column array, passes without problems:
```py
import numpy as np
import eigenpy as eig

v = np.array([[0.5], [0.5], [-0.5], [0.5]])
q = eig.Quaternion(v)
```


Discussion / hints:
Tested on: Ubuntu 20.04, Python 3.8.5, IPython 7.13.0, numpy 1.19.1, Eigen 3.3.7

I couldn't manage to understand where the problem is, because it seems 3-element arrays work fine:
```py
In [7]: eig.Quaternion(np.array([0., 0., 1.]), np.array([1., 0., 0.]))                                                                  
Out[7]: (x,y,z,w) =        0 0.707107        0 0.707107
```
I think it could be easily be fixed by using `VectorXd` as an input and checking for the size, but it looks like a deeper problem with `Vector4` itself
Notice the constructor from a single 4-element `numpy.array` isn't checked in the unit tests
I think I am starting to understand the problem: in C++ there exists a single constructor accepting `MatrixBase` as input, which 
> Constructs and initializes a quaternion from either:
>     a rotation matrix expression,
>     a 4D vector expression representing quaternion coefficients.

https://eigen.tuxfamily.org/dox/classEigen_1_1Quaternion.html#aba694e0bbafcf5554b77cfe8ce69c14a

This is confirmed by the error message:
> The number of rows does not fit with the matrix type.

It says _matrix_ type, not _vector_, indicating it is not recognizing it as a vector at compile time (these are the error messages issued by include/eigenpy/numpy-map.hpp)

So, the array gets treated like a matrix. If I do not expose the constructor from a 3D matrix, the one from a vector works
I've tried working on it with no satisfactory results, I'll leave it to you. I think my comment above is a good start
Who is the you that you mention in the previous message?
> Who is the you that you mention in the previous message?

Well, anybody willing to work on this. I've done my best trying to fix it, sorry, but I am really not sure how to go forward, and also I think the solution may involve design choices which should probably be taken by someone more directly involved in Eigenpy's development

As for myself, I've worked quite a bit in order to identify the problem as narrowly as possible, but I don't have much time for going on with it. There are of course simple ways to go around this problem in my own code, so I think that's how I will deal with this problem for what strictly concerns me. But this is an unexpected bug, I thought you might be interested

Fix patch:
diff --git a/include/eigenpy/quaternion.hpp b/include/eigenpy/quaternion.hpp
index 05d88a64c..c0d237459 100644
--- a/include/eigenpy/quaternion.hpp
+++ b/include/eigenpy/quaternion.hpp
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014-2020 CNRS INRIA
+ * Copyright 2014-2021 CNRS INRIA
  */
 
 #ifndef __eigenpy_quaternion_hpp__
@@ -111,12 +111,12 @@ namespace eigenpy
     {
       cl
       .def(bp::init<>(bp::arg("self"),"Default constructor"))
-      .def(bp::init<Vector4>((bp::arg("self"),bp::arg("vec4")),
-                             "Initialize from a vector 4D.\n"
-                             "\tvec4 : a 4D vector representing quaternion coefficients in the order xyzw."))
       .def(bp::init<Matrix3>((bp::arg("self"),bp::arg("R")),
                              "Initialize from rotation matrix.\n"
                              "\tR : a rotation matrix 3x3."))
+      .def(bp::init<Vector4>((bp::arg("self"),bp::arg("vec4")),
+                             "Initialize from a vector 4D.\n"
+                             "\tvec4 : a 4D vector representing quaternion coefficients in the order xyzw."))
       .def(bp::init<AngleAxis>((bp::arg("self"),bp::arg("aa")),
                                "Initialize from an angle axis.\n"
                                "\taa: angle axis object."))


Test patch:
diff --git a/unittest/python/test_geometry.py b/unittest/python/test_geometry.py
index 818d37f0c..86332f242 100644
--- a/unittest/python/test_geometry.py
+++ b/unittest/python/test_geometry.py
@@ -18,6 +18,10 @@ def isapprox(a,b,epsilon=1e-6):
 assert(isapprox(np.linalg.norm(q.coeffs()),q.norm()))
 assert(isapprox(np.linalg.norm(q.coeffs()),1))
 
+v = np.array([0.5,-0.5,0.5,0.5])
+qv = Quaternion(v)
+assert(isapprox(qv.coeffs(), v))
+
 r = AngleAxis(q)
 q2 = Quaternion(r)
 assert(q==q)