Skip to content

Commit

Permalink
Merge pull request #45 from jcarpent/topic/conversion
Browse files Browse the repository at this point in the history
Handle conversion from Eigen::Matrix to numpy.array or numpy.matrix
  • Loading branch information
jcarpent authored Oct 19, 2018
2 parents 935d313 + b409c63 commit 10da260
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 15 deletions.
57 changes: 57 additions & 0 deletions benchmarks/bench-switch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
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")

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")
62 changes: 50 additions & 12 deletions include/eigenpy/details.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,36 +54,74 @@ namespace eigenpy

namespace bp = boost::python;

struct PyMatrixType
struct NumpyType
{

static PyMatrixType & getInstance()
static NumpyType & getInstance()
{
static PyMatrixType instance;
static NumpyType 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<PyTypeObject*>(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<PyTypeObject*>(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<PyTypeObject*>(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;
}

static void switchToNumpyArray()
{
getInstance().CurrentNumpyType = getInstance().NumpyArrayObject;
}

static void switchToNumpyMatrix()
{
getInstance().CurrentNumpyType = getInstance().NumpyMatrixObject;
}

protected:
PyMatrixType()
NumpyType()
{
pyModule = bp::import("numpy");
pyMatrixType = pyModule.attr("matrix");

NumpyMatrixObject = pyModule.attr("matrix");
NumpyMatrixType = reinterpret_cast<PyTypeObject*>(NumpyMatrixObject.ptr());
NumpyAsMatrixObject = pyModule.attr("asmatrix");
NumpyAsMatrixType = reinterpret_cast<PyTypeObject*>(NumpyAsMatrixObject.ptr());
NumpyArrayObject = pyModule.attr("ndarray");
NumpyArrayType = reinterpret_cast<PyTypeObject*>(NumpyArrayObject.ptr());

CurrentNumpyType = NumpyMatrixObject; // default conversion
}

bp::object pyMatrixType;
bp::object CurrentNumpyType;
bp::object pyModule;

// Numpy types
bp::object NumpyMatrixObject; PyTypeObject * NumpyMatrixType;
bp::object NumpyAsMatrixObject; PyTypeObject * NumpyAsMatrixType;
bp::object NumpyArrayObject; PyTypeObject * NumpyArrayType;
};

template<typename MatType>
Expand Down Expand Up @@ -174,7 +212,7 @@ namespace eigenpy

EigenObjectAllocator<MatType>::convert(mat,pyArray);

return PyMatrixType::getInstance().make(pyArray).ptr();
return NumpyType::getInstance().make(pyArray).ptr();
}
};

Expand Down Expand Up @@ -293,7 +331,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)
{
Expand Down
14 changes: 12 additions & 2 deletions src/eigenpy.cpp
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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",&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",&NumpyType::switchToNumpyArray,
"Set the conversion from Eigen::Matrix to numpy.ndarray.");

bp::def("switchToNumpyMatrix",&NumpyType::switchToNumpyMatrix,
"Set the conversion from Eigen::Matrix to numpy.matrix.");

ENABLE_SPECIFIC_MATRIX_TYPE(MatrixXd);
ENABLE_SPECIFIC_MATRIX_TYPE(Matrix2d);
Expand Down
3 changes: 2 additions & 1 deletion unittest/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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")
18 changes: 18 additions & 0 deletions unittest/python/test_switch.py
Original file line number Diff line number Diff line change
@@ -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)

0 comments on commit 10da260

Please sign in to comment.