From 81c5ae6448636200b3deffa781b7494ce24fd007 Mon Sep 17 00:00:00 2001 From: Justin Carpentier Date: Thu, 18 Oct 2018 18:26:49 +0200 Subject: [PATCH 1/8] numpy: handle the convertion of Eigen::Matrix either as np.matrix or np.ndarray --- include/eigenpy/details.hpp | 40 ++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/include/eigenpy/details.hpp b/include/eigenpy/details.hpp index e80e3dfa2..e7c60e896 100644 --- a/include/eigenpy/details.hpp +++ b/include/eigenpy/details.hpp @@ -56,34 +56,60 @@ namespace eigenpy struct PyMatrixType { - + static PyMatrixType & getInstance() { static PyMatrixType instance; return instance; } - operator bp::object () { return pyMatrixType; } + operator bp::object () { return CurrentNumpyType; } bp::object make(PyArrayObject* pyArray, bool copy = false) { return make((PyObject*)pyArray,copy); } + bp::object make(PyObject* pyObj, bool copy = false) { - bp::object m - = pyMatrixType(bp::object(bp::handle<>(pyObj)), bp::object(), copy); + bp::object m; + if(PyType_IsSubtype(reinterpret_cast(CurrentNumpyType.ptr()),NumpyMatrixType)) + m = NumpyMatrixObject(bp::object(bp::handle<>(pyObj)), bp::object(), copy); + else if(PyType_IsSubtype(reinterpret_cast(CurrentNumpyType.ptr()),NumpyArrayType)) + m = bp::object(bp::handle<>(pyObj)); // nothing to do here + Py_INCREF(m.ptr()); return m; } + + static void setNumpyType(bp::object & obj) + { + PyTypeObject * obj_type = PyType_Check(obj.ptr()) ? reinterpret_cast(obj.ptr()) : obj.ptr()->ob_type; + if(PyType_IsSubtype(obj_type,getInstance().NumpyMatrixType)) + getInstance().CurrentNumpyType = getInstance().NumpyMatrixObject; + else if(PyType_IsSubtype(obj_type,getInstance().NumpyArrayType)) + getInstance().CurrentNumpyType = getInstance().NumpyArrayObject;; + } protected: PyMatrixType() { pyModule = bp::import("numpy"); - pyMatrixType = pyModule.attr("matrix"); + CurrentNumpyType = pyModule.attr("matrix"); // default conversion + + NumpyMatrixObject = pyModule.attr("matrix"); + NumpyMatrixType = reinterpret_cast(NumpyMatrixObject.ptr()); + NumpyArrayObject = pyModule.attr("ndarray"); + NumpyArrayType = reinterpret_cast(NumpyArrayObject.ptr()); } - bp::object pyMatrixType; + bp::object CurrentNumpyType; bp::object pyModule; + + // Numpy types + bp::object NumpyMatrixObject; PyTypeObject * NumpyMatrixType; + bp::object NumpyArrayObject; PyTypeObject * NumpyArrayType; + + +// PyTypeObject * NumpyArrayType; }; template @@ -297,7 +323,7 @@ namespace eigenpy return obj_ptr; } - // Convert obj_ptr into a Eigenvec + // Convert obj_ptr into an Eigen::Vector static void construct(PyObject* pyObj, bp::converter::rvalue_from_python_stage1_data* memory) { From 637b9bb928918a2f9ba5f48b5e96cb7506d4ec16 Mon Sep 17 00:00:00 2001 From: Justin Carpentier Date: Thu, 18 Oct 2018 18:36:26 +0200 Subject: [PATCH 2/8] numpy: add helper for the switch --- include/eigenpy/details.hpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/include/eigenpy/details.hpp b/include/eigenpy/details.hpp index e7c60e896..fea174827 100644 --- a/include/eigenpy/details.hpp +++ b/include/eigenpy/details.hpp @@ -86,19 +86,30 @@ namespace eigenpy if(PyType_IsSubtype(obj_type,getInstance().NumpyMatrixType)) getInstance().CurrentNumpyType = getInstance().NumpyMatrixObject; else if(PyType_IsSubtype(obj_type,getInstance().NumpyArrayType)) - getInstance().CurrentNumpyType = getInstance().NumpyArrayObject;; + getInstance().CurrentNumpyType = getInstance().NumpyArrayObject; + } + + static void switchToNumpyArray() + { + getInstance().CurrentNumpyType = getInstance().NumpyArrayObject; + } + + static void switchToNumpyMatrix() + { + getInstance().CurrentNumpyType = getInstance().NumpyMatrixObject; } protected: PyMatrixType() { pyModule = bp::import("numpy"); - CurrentNumpyType = pyModule.attr("matrix"); // default conversion NumpyMatrixObject = pyModule.attr("matrix"); NumpyMatrixType = reinterpret_cast(NumpyMatrixObject.ptr()); NumpyArrayObject = pyModule.attr("ndarray"); NumpyArrayType = reinterpret_cast(NumpyArrayObject.ptr()); + + CurrentNumpyType = NumpyMatrixObject; // default conversion } bp::object CurrentNumpyType; @@ -107,9 +118,6 @@ namespace eigenpy // Numpy types bp::object NumpyMatrixObject; PyTypeObject * NumpyMatrixType; bp::object NumpyArrayObject; PyTypeObject * NumpyArrayType; - - -// PyTypeObject * NumpyArrayType; }; template From d6539ff652aadb118cdcce8c6e1533966599bdd5 Mon Sep 17 00:00:00 2001 From: Justin Carpentier Date: Thu, 18 Oct 2018 18:37:08 +0200 Subject: [PATCH 3/8] lib: expose the new switching features --- src/eigenpy.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/eigenpy.cpp b/src/eigenpy.cpp index dae15e9d2..df38094ae 100644 --- a/src/eigenpy.cpp +++ b/src/eigenpy.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018 LAAS-CNRS + * Copyright (c) 2015-2018 LAAS-CNRS, INRIA * * This file is part of eigenpy. * eigenpy is free software: you can redistribute it and/or @@ -19,11 +19,21 @@ namespace eigenpy { - /* Enable Eigen-Numpy serialization for a set of standard MatrixBase instance. */ + /* Enable Eigen-Numpy serialization for a set of standard MatrixBase instances. */ void enableEigenPy() { using namespace Eigen; Exception::registerException(); + + bp::def("setNumpyType",&PyMatrixType::setNumpyType, + bp::arg("Numpy type (np.ndarray or np.matrix)"), + "Change the type returned by the converters from an Eigen object."); + + bp::def("switchToNumpyArray",&PyMatrixType::switchToNumpyArray, + "Set the conversion from Eigen::Matrix to numpy.ndarray."); + + bp::def("switchToNumpyMatrix",&PyMatrixType::switchToNumpyMatrix, + "Set the conversion from Eigen::Matrix to numpy.matrix."); ENABLE_SPECIFIC_MATRIX_TYPE(MatrixXd); ENABLE_SPECIFIC_MATRIX_TYPE(Matrix2d); From 28d585b29c1d54f615ad899ddfc0177f4a2a1135 Mon Sep 17 00:00:00 2001 From: Justin Carpentier Date: Thu, 18 Oct 2018 18:46:35 +0200 Subject: [PATCH 4/8] test: add test on switch between np.array and np.matrix --- unittest/CMakeLists.txt | 3 ++- unittest/python/test_switch.py | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 unittest/python/test_switch.py diff --git a/unittest/CMakeLists.txt b/unittest/CMakeLists.txt index f61b4c0df..28f45470e 100644 --- a/unittest/CMakeLists.txt +++ b/unittest/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright (c) 2016-2018 CNRS +# Copyright (c) 2016-2018 CNRS INRIA # # This file is part of eigenpy # eigenpy is free software: you can redistribute it @@ -46,3 +46,4 @@ ENDIF() ADD_PYTHON_UNIT_TEST("py-matrix" "unittest/python/test_matrix.py" "unittest") ADD_PYTHON_UNIT_TEST("py-geometry" "unittest/python/test_geometry.py" "unittest") +ADD_PYTHON_UNIT_TEST("py-switch" "unittest/python/test_switch.py" "unittest") diff --git a/unittest/python/test_switch.py b/unittest/python/test_switch.py new file mode 100644 index 000000000..a68e6fe36 --- /dev/null +++ b/unittest/python/test_switch.py @@ -0,0 +1,18 @@ +from __future__ import print_function + +import eigenpy +import numpy as np + +quat = eigenpy.Quaternion() +# By default, we convert as numpy.matrix +coeffs_vector = quat.coeffs() +print(type(coeffs_vector)) + +assert isinstance(coeffs_vector,np.matrixlib.defmatrix.matrix) + +# Switch to numpy.array +eigenpy.switchToNumpyArray() +coeffs_array = quat.coeffs() +print(type(coeffs_array)) + +assert isinstance(coeffs_vector,np.ndarray) From 0b3366f3c1860e0fd5f34be995024c6765d26dbe Mon Sep 17 00:00:00 2001 From: Justin Carpentier Date: Thu, 18 Oct 2018 19:10:24 +0200 Subject: [PATCH 5/8] bench: add benchmark of switch between np.array and np.matrix --- benchmarks/bench-switch.py | 46 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 benchmarks/bench-switch.py diff --git a/benchmarks/bench-switch.py b/benchmarks/bench-switch.py new file mode 100644 index 000000000..5aa6d1d27 --- /dev/null +++ b/benchmarks/bench-switch.py @@ -0,0 +1,46 @@ +from __future__ import print_function + +import eigenpy +import numpy as np + +import time +import timeit + +from IPython import get_ipython +ipython = get_ipython() + +quat = eigenpy.Quaternion() +a = [0., 0., 0.] + +cmd1 = "timeit np.array(a)" +print("\n") +print(cmd1) +ipython.magic(cmd1) +print("\n") + +cmd2 = "timeit np.matrix(a)" +print(cmd2) +ipython.magic(cmd2) +print("\n") + +eigenpy.switchToNumpyMatrix() +print("----------------------") +print("switch to numpy matrix") +print("----------------------") +print("\n") + +cmd3 = "timeit quat.coeffs()" +print(cmd3) +ipython.magic(cmd3) +print("\n") + +eigenpy.switchToNumpyArray() +print("---------------------") +print("switch to numpy array") +print("---------------------") +print("\n") + +cmd4 = "timeit quat.coeffs()" +print(cmd4) +ipython.magic(cmd4) +print("\n") From e71c4600fdbe5cdd68014339b7f1ef1956e2008f Mon Sep 17 00:00:00 2001 From: Justin Carpentier Date: Thu, 18 Oct 2018 23:41:30 +0200 Subject: [PATCH 6/8] conversion: rename PyMatrixType in NumpyType to reflect change --- include/eigenpy/details.hpp | 10 +++++----- src/eigenpy.cpp | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/eigenpy/details.hpp b/include/eigenpy/details.hpp index fea174827..1ca6578b0 100644 --- a/include/eigenpy/details.hpp +++ b/include/eigenpy/details.hpp @@ -54,12 +54,12 @@ namespace eigenpy namespace bp = boost::python; - struct PyMatrixType + struct NumpyType { - static PyMatrixType & getInstance() + static NumpyType & getInstance() { - static PyMatrixType instance; + static NumpyType instance; return instance; } @@ -100,7 +100,7 @@ namespace eigenpy } protected: - PyMatrixType() + NumpyType() { pyModule = bp::import("numpy"); @@ -208,7 +208,7 @@ namespace eigenpy EigenObjectAllocator::convert(mat,pyArray); - return PyMatrixType::getInstance().make(pyArray).ptr(); + return NumpyType::getInstance().make(pyArray).ptr(); } }; diff --git a/src/eigenpy.cpp b/src/eigenpy.cpp index df38094ae..297646d0c 100644 --- a/src/eigenpy.cpp +++ b/src/eigenpy.cpp @@ -25,14 +25,14 @@ namespace eigenpy using namespace Eigen; Exception::registerException(); - bp::def("setNumpyType",&PyMatrixType::setNumpyType, + bp::def("setNumpyType",&NumpyType::setNumpyType, bp::arg("Numpy type (np.ndarray or np.matrix)"), "Change the type returned by the converters from an Eigen object."); - bp::def("switchToNumpyArray",&PyMatrixType::switchToNumpyArray, + bp::def("switchToNumpyArray",&NumpyType::switchToNumpyArray, "Set the conversion from Eigen::Matrix to numpy.ndarray."); - bp::def("switchToNumpyMatrix",&PyMatrixType::switchToNumpyMatrix, + bp::def("switchToNumpyMatrix",&NumpyType::switchToNumpyMatrix, "Set the conversion from Eigen::Matrix to numpy.matrix."); ENABLE_SPECIFIC_MATRIX_TYPE(MatrixXd); From f1f2bc4477070013b8aeb01a407404e1f5b3594f Mon Sep 17 00:00:00 2001 From: Justin Carpentier Date: Fri, 19 Oct 2018 10:28:10 +0200 Subject: [PATCH 7/8] bench: increase switch benchmark --- benchmarks/bench-switch.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/benchmarks/bench-switch.py b/benchmarks/bench-switch.py index 5aa6d1d27..6f42cc5a5 100644 --- a/benchmarks/bench-switch.py +++ b/benchmarks/bench-switch.py @@ -44,3 +44,14 @@ print(cmd4) ipython.magic(cmd4) print("\n") + +cmd5 = "timeit np.asmatrix(quat.coeffs())" +print(cmd5) +ipython.magic(cmd5) +print("\n") + +a_matrix = np.matrix(a); +cmd6 = "timeit np.asarray(a_matrix)" +print(cmd6) +ipython.magic(cmd6) +print("\n") From b409c63d06580174df14445890080ad2918f3db0 Mon Sep 17 00:00:00 2001 From: Justin Carpentier Date: Fri, 19 Oct 2018 10:28:44 +0200 Subject: [PATCH 8/8] conversion: add asmatrix converter --- include/eigenpy/details.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/eigenpy/details.hpp b/include/eigenpy/details.hpp index 1ca6578b0..03ec4e9dc 100644 --- a/include/eigenpy/details.hpp +++ b/include/eigenpy/details.hpp @@ -73,6 +73,7 @@ namespace eigenpy bp::object m; if(PyType_IsSubtype(reinterpret_cast(CurrentNumpyType.ptr()),NumpyMatrixType)) m = NumpyMatrixObject(bp::object(bp::handle<>(pyObj)), bp::object(), copy); +// m = NumpyAsMatrixObject(bp::object(bp::handle<>(pyObj))); else if(PyType_IsSubtype(reinterpret_cast(CurrentNumpyType.ptr()),NumpyArrayType)) m = bp::object(bp::handle<>(pyObj)); // nothing to do here @@ -106,6 +107,8 @@ namespace eigenpy NumpyMatrixObject = pyModule.attr("matrix"); NumpyMatrixType = reinterpret_cast(NumpyMatrixObject.ptr()); + NumpyAsMatrixObject = pyModule.attr("asmatrix"); + NumpyAsMatrixType = reinterpret_cast(NumpyAsMatrixObject.ptr()); NumpyArrayObject = pyModule.attr("ndarray"); NumpyArrayType = reinterpret_cast(NumpyArrayObject.ptr()); @@ -117,6 +120,7 @@ namespace eigenpy // Numpy types bp::object NumpyMatrixObject; PyTypeObject * NumpyMatrixType; + bp::object NumpyAsMatrixObject; PyTypeObject * NumpyAsMatrixType; bp::object NumpyArrayObject; PyTypeObject * NumpyArrayType; };