mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-22 05:05:11 +00:00
transparent conversion of dense and sparse Eigen types
This commit is contained in:
parent
9ac5bc5531
commit
9e0a0568fe
@ -84,6 +84,11 @@ elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" OR "${CMAKE_CXX_COMPILER_ID}"
|
|||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
# Check if Eigen is available
|
||||||
|
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/tools")
|
||||||
|
find_package(Eigen3 QUIET)
|
||||||
|
|
||||||
# Include path for pybind11 header files
|
# Include path for pybind11 header files
|
||||||
include_directories(include)
|
include_directories(include)
|
||||||
|
|
||||||
@ -96,6 +101,7 @@ set(PYBIND11_HEADERS
|
|||||||
include/pybind11/common.h
|
include/pybind11/common.h
|
||||||
include/pybind11/complex.h
|
include/pybind11/complex.h
|
||||||
include/pybind11/descr.h
|
include/pybind11/descr.h
|
||||||
|
include/pybind11/eigen.h
|
||||||
include/pybind11/functional.h
|
include/pybind11/functional.h
|
||||||
include/pybind11/numpy.h
|
include/pybind11/numpy.h
|
||||||
include/pybind11/operators.h
|
include/pybind11/operators.h
|
||||||
@ -125,6 +131,15 @@ set(PYBIND11_EXAMPLES
|
|||||||
example/issues.cpp
|
example/issues.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (EIGEN3_FOUND)
|
||||||
|
include_directories(${EIGEN3_INCLUDE_DIR})
|
||||||
|
list(APPEND PYBIND11_EXAMPLES example/eigen.cpp)
|
||||||
|
add_definitions(-DPYBIND11_TEST_EIGEN)
|
||||||
|
message(STATUS "Building Eigen testcase")
|
||||||
|
else()
|
||||||
|
message(STATUS "NOT Building Eigen testcase")
|
||||||
|
endif()
|
||||||
|
|
||||||
# Create the binding library
|
# Create the binding library
|
||||||
add_library(example SHARED
|
add_library(example SHARED
|
||||||
${PYBIND11_HEADERS}
|
${PYBIND11_HEADERS}
|
||||||
|
@ -792,13 +792,157 @@ There is also a special exception :class:`cast_error` that is thrown by
|
|||||||
:func:`handle::call` when the input arguments cannot be converted to Python
|
:func:`handle::call` when the input arguments cannot be converted to Python
|
||||||
objects.
|
objects.
|
||||||
|
|
||||||
|
.. _opaque:
|
||||||
|
|
||||||
|
Treating STL data structures as opaque objects
|
||||||
|
==============================================
|
||||||
|
|
||||||
|
pybind11 heavily relies on a template matching mechanism to convert parameters
|
||||||
|
and return values that are constructed from STL data types such as vectors,
|
||||||
|
linked lists, hash tables, etc. This even works in a recursive manner, for
|
||||||
|
instance to deal with lists of hash maps of pairs of elementary and custom
|
||||||
|
types, etc.
|
||||||
|
|
||||||
|
However, a fundamental limitation of this approach is that internal conversions
|
||||||
|
between Python and C++ types involve a copy operation that prevents
|
||||||
|
pass-by-reference semantics. What does this mean?
|
||||||
|
|
||||||
|
Suppose we bind the following function
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
void append_1(std::vector<int> &v) {
|
||||||
|
v.push_back(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
and call it from Python, the following happens:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
>>> v = [5, 6]
|
||||||
|
>>> append_1(v)
|
||||||
|
>>> print(v)
|
||||||
|
[5, 6]
|
||||||
|
|
||||||
|
As you can see, when passing STL data structures by reference, modifications
|
||||||
|
are not propagated back the Python side. A similar situation arises when
|
||||||
|
exposing STL data structures using the ``def_readwrite`` or ``def_readonly``
|
||||||
|
functions:
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
/* ... definition ... */
|
||||||
|
|
||||||
|
class MyClass {
|
||||||
|
std::vector<int> contents;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ... binding code ... */
|
||||||
|
|
||||||
|
py::class_<MyClass>(m, "MyClass")
|
||||||
|
.def(py::init<>)
|
||||||
|
.def_readwrite("contents", &MyClass::contents);
|
||||||
|
|
||||||
|
In this case, properties can be read and written in their entirety. However, an
|
||||||
|
``append`` operaton involving such a list type has no effect:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
>>> m = MyClass()
|
||||||
|
>>> m.contents = [5, 6]
|
||||||
|
>>> print(m.contents)
|
||||||
|
[5, 6]
|
||||||
|
>>> m.contents.append(7)
|
||||||
|
>>> print(m.contents)
|
||||||
|
[5, 6]
|
||||||
|
|
||||||
|
To deal with both of the above situations, pybind11 provides a macro named
|
||||||
|
``PYBIND11_MAKE_OPAQUE(T)`` that disables the template-based conversion
|
||||||
|
machinery of types, thus rendering them *opaque*. The contents of opaque
|
||||||
|
objects are never inspected or extracted, hence they can be passed by
|
||||||
|
reference. For instance, to turn ``std::vector<int>`` into an opaque type, add
|
||||||
|
the declaration
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
PYBIND11_MAKE_OPAQUE(std::vector<int>);
|
||||||
|
|
||||||
|
before any binding code (e.g. invocations to ``class_::def()``, etc.). This
|
||||||
|
macro must be specified at the top level, since instantiates a partial template
|
||||||
|
overload. If your binding code consists of multiple compilation units, it must
|
||||||
|
be present in every file preceding any usage of ``std::vector<int>``. Opaque
|
||||||
|
types must also have a corresponding ``class_`` declaration to associate them
|
||||||
|
with a name in Python, and to define a set of available operations:
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
py::class_<std::vector<int>>(m, "IntVector")
|
||||||
|
.def(py::init<>())
|
||||||
|
.def("clear", &std::vector<int>::clear)
|
||||||
|
.def("pop_back", &std::vector<int>::pop_back)
|
||||||
|
.def("__len__", [](const std::vector<int> &v) { return v.size(); })
|
||||||
|
.def("__iter__", [](std::vector<int> &v) {
|
||||||
|
return py::make_iterator(v.begin(), v.end());
|
||||||
|
}, py::keep_alive<0, 1>()) /* Keep vector alive while iterator is used */
|
||||||
|
// ....
|
||||||
|
|
||||||
|
|
||||||
|
.. seealso::
|
||||||
|
|
||||||
|
The file :file:`example/example14.cpp` contains a complete example that
|
||||||
|
demonstrates how to create and expose opaque types using pybind11 in more
|
||||||
|
detail.
|
||||||
|
|
||||||
|
.. _eigen:
|
||||||
|
|
||||||
|
Transparent conversion of dense and sparse Eigen data types
|
||||||
|
===========================================================
|
||||||
|
|
||||||
|
Eigen [#f1]_ is C++ header-based library for dense and sparse linear algebra. Due to
|
||||||
|
its popularity and widespread adoption, pybind11 provides transparent
|
||||||
|
conversion support between Eigen and Scientific Python linear algebra data types.
|
||||||
|
|
||||||
|
Specifically, when including the optional header file :file:`pybind11/eigen.h`,
|
||||||
|
pybind11 will automatically and transparently convert
|
||||||
|
|
||||||
|
1. Static and dynamic Eigen dense vectors and matrices to instances of
|
||||||
|
``numpy.ndarray`` (and vice versa).
|
||||||
|
|
||||||
|
1. Eigen sparse vectors and matrices to instances of
|
||||||
|
``scipy.sparse.csr_matrix``/``scipy.sparse.csc_matrix`` (and vice versa).
|
||||||
|
|
||||||
|
This makes it possible to bind most kinds of functions that rely on these types.
|
||||||
|
One major caveat are functions that take Eigen matrices *by reference* and modify
|
||||||
|
them somehow, in which case the information won't be propagated to the caller.
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
/* The Python bindings of this function won't replicate
|
||||||
|
the intended effect of modifying the function argument */
|
||||||
|
void scale_by_2(Eigen::Vector3f &v) {
|
||||||
|
v *= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
To see why this is, refer to the section on :ref:`opaque` (although that
|
||||||
|
section specifically covers STL data types, the underlying issue is the same).
|
||||||
|
The next two sections discuss an efficient alternative for exposing the
|
||||||
|
underlying native Eigen types as opaque objects in a way that still integrates
|
||||||
|
with NumPy and SciPy.
|
||||||
|
|
||||||
|
.. [#f1] http://eigen.tuxfamily.org
|
||||||
|
|
||||||
|
.. seealso::
|
||||||
|
|
||||||
|
The file :file:`example/eigen.cpp` contains a complete example that
|
||||||
|
shows how to pass Eigen sparse and dense data types in more detail.
|
||||||
|
|
||||||
Buffer protocol
|
Buffer protocol
|
||||||
===============
|
===============
|
||||||
|
|
||||||
Python supports an extremely general and convenient approach for exchanging
|
Python supports an extremely general and convenient approach for exchanging
|
||||||
data between plugin libraries. Types can expose a buffer view [#f1]_,
|
data between plugin libraries. Types can expose a buffer view [#f2]_, which
|
||||||
which provides fast direct access to the raw internal representation. Suppose
|
provides fast direct access to the raw internal data representation. Suppose we
|
||||||
we want to bind the following simplistic Matrix class:
|
want to bind the following simplistic Matrix class:
|
||||||
|
|
||||||
.. code-block:: cpp
|
.. code-block:: cpp
|
||||||
|
|
||||||
@ -856,16 +1000,19 @@ in a great variety of configurations, hence some safety checks are usually
|
|||||||
necessary in the function body. Below, you can see an basic example on how to
|
necessary in the function body. Below, you can see an basic example on how to
|
||||||
define a custom constructor for the Eigen double precision matrix
|
define a custom constructor for the Eigen double precision matrix
|
||||||
(``Eigen::MatrixXd``) type, which supports initialization from compatible
|
(``Eigen::MatrixXd``) type, which supports initialization from compatible
|
||||||
buffer
|
buffer objects (e.g. a NumPy matrix).
|
||||||
objects (e.g. a NumPy matrix).
|
|
||||||
|
|
||||||
.. code-block:: cpp
|
.. code-block:: cpp
|
||||||
|
|
||||||
py::class_<Eigen::MatrixXd>(m, "MatrixXd")
|
/* Bind MatrixXd (or some other Eigen type) to Python */
|
||||||
.def("__init__", [](Eigen::MatrixXd &m, py::buffer b) {
|
typedef Eigen::MatrixXd Matrix;
|
||||||
|
|
||||||
|
typedef Matrix::Scalar Scalar;
|
||||||
|
constexpr bool rowMajor = Matrix::Flags & Eigen::RowMajorBit;
|
||||||
|
|
||||||
|
py::class_<Matrix>(m, "Matrix")
|
||||||
|
.def("__init__", [](Matrix &m, py::buffer b) {
|
||||||
typedef Eigen::Stride<Eigen::Dynamic, Eigen::Dynamic> Strides;
|
typedef Eigen::Stride<Eigen::Dynamic, Eigen::Dynamic> Strides;
|
||||||
typedef Eigen::MatrixXd Matrix;
|
|
||||||
typedef Matrix::Scalar Scalar;
|
|
||||||
|
|
||||||
/* Request a buffer descriptor from Python */
|
/* Request a buffer descriptor from Python */
|
||||||
py::buffer_info info = b.request();
|
py::buffer_info info = b.request();
|
||||||
@ -878,21 +1025,46 @@ objects (e.g. a NumPy matrix).
|
|||||||
throw std::runtime_error("Incompatible buffer dimension!");
|
throw std::runtime_error("Incompatible buffer dimension!");
|
||||||
|
|
||||||
auto strides = Strides(
|
auto strides = Strides(
|
||||||
info.strides[Matrix::Flags & Eigen::RowMajorBit ? 0 : 1] / sizeof(Scalar),
|
info.strides[rowMajor ? 0 : 1] / sizeof(Scalar),
|
||||||
info.strides[Matrix::Flags & Eigen::RowMajorBit ? 1 : 0] / sizeof(Scalar));
|
info.strides[rowMajor ? 1 : 0] / sizeof(Scalar));
|
||||||
|
|
||||||
auto map = Eigen::Map<Matrix, 0, Strides>(
|
auto map = Eigen::Map<Matrix, 0, Strides>(
|
||||||
(Scalar *) info.ptr, info.shape[0], info.shape[1], strides);
|
static_cat<Scalar *>(info.ptr), info.shape[0], info.shape[1], strides);
|
||||||
|
|
||||||
new (&m) Matrix(map);
|
new (&m) Matrix(map);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
For reference, the ``def_buffer()`` call for this Eigen data type should look
|
||||||
|
as follows:
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
.def_buffer([](Matrix &m) -> py::buffer_info {
|
||||||
|
return py::buffer_info(
|
||||||
|
m.data(), /* Pointer to buffer */
|
||||||
|
sizeof(Scalar), /* Size of one scalar */
|
||||||
|
/* Python struct-style format descriptor */
|
||||||
|
py::format_descriptor<Scalar>::value,
|
||||||
|
/* Number of dimensions */
|
||||||
|
2,
|
||||||
|
/* Buffer dimensions */
|
||||||
|
{ (size_t) m.rows(),
|
||||||
|
(size_t) m.cols() },
|
||||||
|
/* Strides (in bytes) for each index */
|
||||||
|
{ sizeof(Scalar) * (rowMajor ? m.cols() : 1),
|
||||||
|
sizeof(Scalar) * (rowMajor ? 1 : m.rows()) }
|
||||||
|
);
|
||||||
|
})
|
||||||
|
|
||||||
|
For a much easier approach of binding Eigen types (although with some
|
||||||
|
limitations), refer to the section on :ref:`eigen`.
|
||||||
|
|
||||||
.. seealso::
|
.. seealso::
|
||||||
|
|
||||||
The file :file:`example/example7.cpp` contains a complete example that
|
The file :file:`example/example7.cpp` contains a complete example that
|
||||||
demonstrates using the buffer protocol with pybind11 in more detail.
|
demonstrates using the buffer protocol with pybind11 in more detail.
|
||||||
|
|
||||||
.. [#f1] http://docs.python.org/3/c-api/buffer.html
|
.. [#f2] http://docs.python.org/3/c-api/buffer.html
|
||||||
|
|
||||||
NumPy support
|
NumPy support
|
||||||
=============
|
=============
|
||||||
@ -1199,105 +1371,6 @@ accessed by multiple extension modules:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Treating STL data structures as opaque objects
|
|
||||||
==============================================
|
|
||||||
|
|
||||||
pybind11 heavily relies on a template matching mechanism to convert parameters
|
|
||||||
and return values that are constructed from STL data types such as vectors,
|
|
||||||
linked lists, hash tables, etc. This even works in a recursive manner, for
|
|
||||||
instance to deal with lists of hash maps of pairs of elementary and custom
|
|
||||||
types, etc.
|
|
||||||
|
|
||||||
However, a fundamental limitation of this approach is that internal conversions
|
|
||||||
between Python and C++ types involve a copy operation that prevents
|
|
||||||
pass-by-reference semantics. What does this mean?
|
|
||||||
|
|
||||||
Suppose we bind the following function
|
|
||||||
|
|
||||||
.. code-block:: cpp
|
|
||||||
|
|
||||||
void append_1(std::vector<int> &v) {
|
|
||||||
v.push_back(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
and call it from Python, the following happens:
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
>>> v = [5, 6]
|
|
||||||
>>> append_1(v)
|
|
||||||
>>> print(v)
|
|
||||||
[5, 6]
|
|
||||||
|
|
||||||
As you can see, when passing STL data structures by reference, modifications
|
|
||||||
are not propagated back the Python side. A similar situation arises when
|
|
||||||
exposing STL data structures using the ``def_readwrite`` or ``def_readonly``
|
|
||||||
functions:
|
|
||||||
|
|
||||||
.. code-block:: cpp
|
|
||||||
|
|
||||||
/* ... definition ... */
|
|
||||||
|
|
||||||
class MyClass {
|
|
||||||
std::vector<int> contents;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* ... binding code ... */
|
|
||||||
|
|
||||||
py::class_<MyClass>(m, "MyClass")
|
|
||||||
.def(py::init<>)
|
|
||||||
.def_readwrite("contents", &MyClass::contents);
|
|
||||||
|
|
||||||
In this case, properties can be read and written in their entirety. However, an
|
|
||||||
``append`` operaton involving such a list type has no effect:
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
>>> m = MyClass()
|
|
||||||
>>> m.contents = [5, 6]
|
|
||||||
>>> print(m.contents)
|
|
||||||
[5, 6]
|
|
||||||
>>> m.contents.append(7)
|
|
||||||
>>> print(m.contents)
|
|
||||||
[5, 6]
|
|
||||||
|
|
||||||
To deal with both of the above situations, pybind11 provides a macro named
|
|
||||||
``PYBIND11_MAKE_OPAQUE(T)`` that disables the template-based conversion
|
|
||||||
machinery of types, thus rendering them *opaque*. The contents of opaque
|
|
||||||
objects are never inspected or extracted, hence they can be passed by
|
|
||||||
reference. For instance, to turn ``std::vector<int>`` into an opaque type, add
|
|
||||||
the declaration
|
|
||||||
|
|
||||||
.. code-block:: cpp
|
|
||||||
|
|
||||||
PYBIND11_MAKE_OPAQUE(std::vector<int>);
|
|
||||||
|
|
||||||
before any binding code (e.g. invocations to ``class_::def()``, etc.). This
|
|
||||||
macro must be specified at the top level, since instantiates a partial template
|
|
||||||
overload. If your binding code consists of multiple compilation units, it must
|
|
||||||
be present in every file preceding any usage of ``std::vector<int>``. Opaque
|
|
||||||
types must also have a corresponding ``class_`` declaration to associate them
|
|
||||||
with a name in Python, and to define a set of available operations:
|
|
||||||
|
|
||||||
.. code-block:: cpp
|
|
||||||
|
|
||||||
py::class_<std::vector<int>>(m, "IntVector")
|
|
||||||
.def(py::init<>())
|
|
||||||
.def("clear", &std::vector<int>::clear)
|
|
||||||
.def("pop_back", &std::vector<int>::pop_back)
|
|
||||||
.def("__len__", [](const std::vector<int> &v) { return v.size(); })
|
|
||||||
.def("__iter__", [](std::vector<int> &v) {
|
|
||||||
return py::make_iterator(v.begin(), v.end());
|
|
||||||
}, py::keep_alive<0, 1>()) /* Keep vector alive while iterator is used */
|
|
||||||
// ....
|
|
||||||
|
|
||||||
|
|
||||||
.. seealso::
|
|
||||||
|
|
||||||
The file :file:`example/example14.cpp` contains a complete example that
|
|
||||||
demonstrates how to create and expose opaque types using pybind11 in more
|
|
||||||
detail.
|
|
||||||
|
|
||||||
Pickling support
|
Pickling support
|
||||||
================
|
================
|
||||||
|
|
||||||
@ -1320,7 +1393,7 @@ Suppose the class in question has the following signature:
|
|||||||
int m_extra = 0;
|
int m_extra = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
The binding code including the requisite ``__setstate__`` and ``__getstate__`` methods [#f2]_
|
The binding code including the requisite ``__setstate__`` and ``__getstate__`` methods [#f3]_
|
||||||
looks as follows:
|
looks as follows:
|
||||||
|
|
||||||
.. code-block:: cpp
|
.. code-block:: cpp
|
||||||
@ -1372,14 +1445,14 @@ memory corruption and/or segmentation faults.
|
|||||||
The file :file:`example/example15.cpp` contains a complete example that
|
The file :file:`example/example15.cpp` contains a complete example that
|
||||||
demonstrates how to pickle and unpickle types using pybind11 in more detail.
|
demonstrates how to pickle and unpickle types using pybind11 in more detail.
|
||||||
|
|
||||||
.. [#f2] http://docs.python.org/3/library/pickle.html#pickling-class-instances
|
.. [#f3] http://docs.python.org/3/library/pickle.html#pickling-class-instances
|
||||||
|
|
||||||
Generating documentation using Sphinx
|
Generating documentation using Sphinx
|
||||||
=====================================
|
=====================================
|
||||||
|
|
||||||
Sphinx [#f3]_ has the ability to inspect the signatures and documentation
|
Sphinx [#f4]_ has the ability to inspect the signatures and documentation
|
||||||
strings in pybind11-based extension modules to automatically generate beautiful
|
strings in pybind11-based extension modules to automatically generate beautiful
|
||||||
documentation in a variety formats. The pbtest repository [#f4]_ contains a
|
documentation in a variety formats. The pbtest repository [#f5]_ contains a
|
||||||
simple example repository which uses this approach.
|
simple example repository which uses this approach.
|
||||||
|
|
||||||
There are two potential gotchas when using this approach: first, make sure that
|
There are two potential gotchas when using this approach: first, make sure that
|
||||||
@ -1406,6 +1479,6 @@ work, it is important that all lines are indented consistently, i.e.:
|
|||||||
----------
|
----------
|
||||||
)mydelimiter");
|
)mydelimiter");
|
||||||
|
|
||||||
.. [#f3] http://www.sphinx-doc.org
|
.. [#f4] http://www.sphinx-doc.org
|
||||||
.. [#f4] http://github.com/pybind/pbtest
|
.. [#f5] http://github.com/pybind/pbtest
|
||||||
|
|
||||||
|
@ -275,6 +275,10 @@ as arguments and return values, refer to the section on binding :ref:`classes`.
|
|||||||
+---------------------------------+--------------------------+-------------------------------+
|
+---------------------------------+--------------------------+-------------------------------+
|
||||||
| ``std::function<...>`` | STL polymorphic function | :file:`pybind11/functional.h` |
|
| ``std::function<...>`` | STL polymorphic function | :file:`pybind11/functional.h` |
|
||||||
+---------------------------------+--------------------------+-------------------------------+
|
+---------------------------------+--------------------------+-------------------------------+
|
||||||
|
| ``Eigen::Matrix<...>`` | Dense Eigen matrices | :file:`pybind11/eigen.h` |
|
||||||
|
+---------------------------------+--------------------------+-------------------------------+
|
||||||
|
| ``Eigen::SparseMatrix<...>`` | Sparse Eigen matrices | :file:`pybind11/eigen.h` |
|
||||||
|
+---------------------------------+--------------------------+-------------------------------+
|
||||||
|
|
||||||
|
|
||||||
.. [#f1] In practice, implementation and binding code will generally be located
|
.. [#f1] In practice, implementation and binding code will generally be located
|
||||||
|
@ -5,6 +5,7 @@ Changelog
|
|||||||
|
|
||||||
1.8 (Not yet released)
|
1.8 (Not yet released)
|
||||||
----------------------
|
----------------------
|
||||||
|
* Transparent conversion of sparse and dense Eigen data types
|
||||||
* Fixed incorrect default return value policy for functions returning a shared
|
* Fixed incorrect default return value policy for functions returning a shared
|
||||||
pointer
|
pointer
|
||||||
* Don't allow casting a ``None`` value into a C++ lvalue reference
|
* Don't allow casting a ``None`` value into a C++ lvalue reference
|
||||||
|
73
example/eigen.cpp
Normal file
73
example/eigen.cpp
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
example/eigen.cpp -- automatic conversion of Eigen types
|
||||||
|
|
||||||
|
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
|
||||||
|
|
||||||
|
All rights reserved. Use of this source code is governed by a
|
||||||
|
BSD-style license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "example.h"
|
||||||
|
#include <pybind11/eigen.h>
|
||||||
|
|
||||||
|
void init_eigen(py::module &m) {
|
||||||
|
typedef Eigen::Matrix<float, 5, 6, Eigen::RowMajor> FixedMatrixR;
|
||||||
|
typedef Eigen::Matrix<float, 5, 6> FixedMatrixC;
|
||||||
|
typedef Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> DenseMatrixR;
|
||||||
|
typedef Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> DenseMatrixC;
|
||||||
|
typedef Eigen::SparseMatrix<float, Eigen::RowMajor> SparseMatrixR;
|
||||||
|
typedef Eigen::SparseMatrix<float> SparseMatrixC;
|
||||||
|
|
||||||
|
// Non-symmetric matrix with zero elements
|
||||||
|
Eigen::MatrixXf mat(5, 6);
|
||||||
|
mat << 0, 3, 0, 0, 0, 11, 22, 0, 0, 0, 17, 11, 7, 5, 0, 1, 0, 11, 0,
|
||||||
|
0, 0, 0, 0, 11, 0, 0, 14, 0, 8, 11;
|
||||||
|
|
||||||
|
m.def("fixed_r", [mat]() -> FixedMatrixR {
|
||||||
|
return FixedMatrixR(mat);
|
||||||
|
});
|
||||||
|
|
||||||
|
m.def("fixed_c", [mat]() -> FixedMatrixC {
|
||||||
|
return FixedMatrixC(mat);
|
||||||
|
});
|
||||||
|
|
||||||
|
m.def("fixed_passthrough_r", [](const FixedMatrixR &m) -> FixedMatrixR {
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
|
||||||
|
m.def("fixed_passthrough_c", [](const FixedMatrixC &m) -> FixedMatrixC {
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
|
||||||
|
m.def("dense_r", [mat]() -> DenseMatrixR {
|
||||||
|
return DenseMatrixR(mat);
|
||||||
|
});
|
||||||
|
|
||||||
|
m.def("dense_c", [mat]() -> DenseMatrixC {
|
||||||
|
return DenseMatrixC(mat);
|
||||||
|
});
|
||||||
|
|
||||||
|
m.def("dense_passthrough_r", [](const DenseMatrixR &m) -> DenseMatrixR {
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
|
||||||
|
m.def("dense_passthrough_c", [](const DenseMatrixC &m) -> DenseMatrixC {
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
|
||||||
|
m.def("sparse_r", [mat]() -> SparseMatrixR {
|
||||||
|
return Eigen::SparseView<Eigen::MatrixXf>(mat);
|
||||||
|
});
|
||||||
|
|
||||||
|
m.def("sparse_c", [mat]() -> SparseMatrixC {
|
||||||
|
return Eigen::SparseView<Eigen::MatrixXf>(mat);
|
||||||
|
});
|
||||||
|
|
||||||
|
m.def("sparse_passthrough_r", [](const SparseMatrixR &m) -> SparseMatrixR {
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
|
||||||
|
m.def("sparse_passthrough_c", [](const SparseMatrixC &m) -> SparseMatrixC {
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
}
|
44
example/eigen.py
Normal file
44
example/eigen.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
from __future__ import print_function
|
||||||
|
import sys
|
||||||
|
sys.path.append('.')
|
||||||
|
|
||||||
|
from example import fixed_r, fixed_c
|
||||||
|
from example import fixed_passthrough_r, fixed_passthrough_c
|
||||||
|
from example import dense_r, dense_c
|
||||||
|
from example import dense_passthrough_r, dense_passthrough_c
|
||||||
|
from example import sparse_r, sparse_c
|
||||||
|
from example import sparse_passthrough_r, sparse_passthrough_c
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
ref = np.array(
|
||||||
|
[[0, 3, 0, 0, 0, 11],
|
||||||
|
[22, 0, 0, 0, 17, 11],
|
||||||
|
[7, 5, 0, 1, 0, 11],
|
||||||
|
[0, 0, 0, 0, 0, 11],
|
||||||
|
[0, 0, 14, 0, 8, 11]])
|
||||||
|
|
||||||
|
|
||||||
|
def check(mat):
|
||||||
|
return 'OK' if np.sum(mat - ref) == 0 else 'NOT OK'
|
||||||
|
|
||||||
|
print("fixed_r = %s" % check(fixed_r()))
|
||||||
|
print("fixed_c = %s" % check(fixed_c()))
|
||||||
|
print("pt_r(fixed_r) = %s" % check(fixed_passthrough_r(fixed_r())))
|
||||||
|
print("pt_c(fixed_c) = %s" % check(fixed_passthrough_c(fixed_c())))
|
||||||
|
print("pt_r(fixed_c) = %s" % check(fixed_passthrough_r(fixed_c())))
|
||||||
|
print("pt_c(fixed_r) = %s" % check(fixed_passthrough_c(fixed_r())))
|
||||||
|
|
||||||
|
print("dense_r = %s" % check(dense_r()))
|
||||||
|
print("dense_c = %s" % check(dense_c()))
|
||||||
|
print("pt_r(dense_r) = %s" % check(dense_passthrough_r(dense_r())))
|
||||||
|
print("pt_c(dense_c) = %s" % check(dense_passthrough_c(dense_c())))
|
||||||
|
print("pt_r(dense_c) = %s" % check(dense_passthrough_r(dense_c())))
|
||||||
|
print("pt_c(dense_r) = %s" % check(dense_passthrough_c(dense_r())))
|
||||||
|
|
||||||
|
print("sparse_r = %s" % check(sparse_r()))
|
||||||
|
print("sparse_c = %s" % check(sparse_c()))
|
||||||
|
print("pt_r(sparse_r) = %s" % check(sparse_passthrough_r(sparse_r())))
|
||||||
|
print("pt_c(sparse_c) = %s" % check(sparse_passthrough_c(sparse_c())))
|
||||||
|
print("pt_r(sparse_c) = %s" % check(sparse_passthrough_r(sparse_c())))
|
||||||
|
print("pt_c(sparse_r) = %s" % check(sparse_passthrough_c(sparse_r())))
|
18
example/eigen.ref
Normal file
18
example/eigen.ref
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
fixed_r = OK
|
||||||
|
fixed_c = OK
|
||||||
|
pt_r(fixed_r) = OK
|
||||||
|
pt_c(fixed_c) = OK
|
||||||
|
pt_r(fixed_c) = OK
|
||||||
|
pt_c(fixed_r) = OK
|
||||||
|
dense_r = OK
|
||||||
|
dense_c = OK
|
||||||
|
pt_r(dense_r) = OK
|
||||||
|
pt_c(dense_c) = OK
|
||||||
|
pt_r(dense_c) = OK
|
||||||
|
pt_c(dense_r) = OK
|
||||||
|
sparse_r = OK
|
||||||
|
sparse_c = OK
|
||||||
|
pt_r(sparse_r) = OK
|
||||||
|
pt_c(sparse_c) = OK
|
||||||
|
pt_r(sparse_c) = OK
|
||||||
|
pt_c(sparse_r) = OK
|
@ -27,6 +27,10 @@ void init_ex15(py::module &);
|
|||||||
void init_ex16(py::module &);
|
void init_ex16(py::module &);
|
||||||
void init_issues(py::module &);
|
void init_issues(py::module &);
|
||||||
|
|
||||||
|
#if defined(PYBIND11_TEST_EIGEN)
|
||||||
|
void init_eigen(py::module &);
|
||||||
|
#endif
|
||||||
|
|
||||||
PYBIND11_PLUGIN(example) {
|
PYBIND11_PLUGIN(example) {
|
||||||
py::module m("example", "pybind example plugin");
|
py::module m("example", "pybind example plugin");
|
||||||
|
|
||||||
@ -48,5 +52,9 @@ PYBIND11_PLUGIN(example) {
|
|||||||
init_ex16(m);
|
init_ex16(m);
|
||||||
init_issues(m);
|
init_issues(m);
|
||||||
|
|
||||||
|
#if defined(PYBIND11_TEST_EIGEN)
|
||||||
|
init_eigen(m);
|
||||||
|
#endif
|
||||||
|
|
||||||
return m.ptr();
|
return m.ptr();
|
||||||
}
|
}
|
||||||
|
261
include/pybind11/eigen.h
Normal file
261
include/pybind11/eigen.h
Normal file
@ -0,0 +1,261 @@
|
|||||||
|
/*
|
||||||
|
pybind11/eigen.h: Transparent conversion for dense and sparse Eigen matrices
|
||||||
|
|
||||||
|
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
|
||||||
|
|
||||||
|
All rights reserved. Use of this source code is governed by a
|
||||||
|
BSD-style license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "numpy.h"
|
||||||
|
#include <Eigen/Core>
|
||||||
|
#include <Eigen/SparseCore>
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
|
||||||
|
#endif
|
||||||
|
|
||||||
|
NAMESPACE_BEGIN(pybind11)
|
||||||
|
NAMESPACE_BEGIN(detail)
|
||||||
|
|
||||||
|
template <typename T> class is_eigen_dense {
|
||||||
|
private:
|
||||||
|
template<typename Derived> static std::true_type test(const Eigen::DenseBase<Derived> &);
|
||||||
|
static std::false_type test(...);
|
||||||
|
public:
|
||||||
|
static constexpr bool value = decltype(test(std::declval<T>()))::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> class is_eigen_sparse {
|
||||||
|
private:
|
||||||
|
template<typename Derived> static std::true_type test(const Eigen::SparseMatrixBase<Derived> &);
|
||||||
|
static std::false_type test(...);
|
||||||
|
public:
|
||||||
|
static constexpr bool value = decltype(test(std::declval<T>()))::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Type>
|
||||||
|
struct type_caster<Type, typename std::enable_if<is_eigen_dense<Type>::value>::type> {
|
||||||
|
typedef typename Type::Scalar Scalar;
|
||||||
|
static constexpr bool rowMajor = Type::Flags & Eigen::RowMajorBit;
|
||||||
|
|
||||||
|
bool load(handle src, bool) {
|
||||||
|
array_t<Scalar> buffer(src, true);
|
||||||
|
if (!buffer.check())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
buffer_info info = buffer.request();
|
||||||
|
if (info.ndim == 1) {
|
||||||
|
typedef Eigen::Stride<Eigen::Dynamic, 0> Strides;
|
||||||
|
if (!Type::IsVectorAtCompileTime &&
|
||||||
|
!(Type::RowsAtCompileTime == Eigen::Dynamic &&
|
||||||
|
Type::ColsAtCompileTime == Eigen::Dynamic))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (Type::SizeAtCompileTime != Eigen::Dynamic &&
|
||||||
|
info.shape[0] != (size_t) Type::SizeAtCompileTime)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto strides = Strides(info.strides[0] / sizeof(Scalar), 0);
|
||||||
|
|
||||||
|
value = Eigen::Map<Type, 0, Strides>(
|
||||||
|
(Scalar *) info.ptr, info.shape[0], 1, strides);
|
||||||
|
} else if (info.ndim == 2) {
|
||||||
|
typedef Eigen::Stride<Eigen::Dynamic, Eigen::Dynamic> Strides;
|
||||||
|
|
||||||
|
if ((Type::RowsAtCompileTime != Eigen::Dynamic && info.shape[0] != (size_t) Type::RowsAtCompileTime) ||
|
||||||
|
(Type::ColsAtCompileTime != Eigen::Dynamic && info.shape[1] != (size_t) Type::ColsAtCompileTime))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto strides = Strides(
|
||||||
|
info.strides[rowMajor ? 0 : 1] / sizeof(Scalar),
|
||||||
|
info.strides[rowMajor ? 1 : 0] / sizeof(Scalar));
|
||||||
|
|
||||||
|
value = Eigen::Map<Type, 0, Strides>(
|
||||||
|
(Scalar *) info.ptr, info.shape[0], info.shape[1], strides);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static handle cast(const Type *src, return_value_policy policy, handle parent) {
|
||||||
|
return cast(*src, policy, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) {
|
||||||
|
array result(buffer_info(
|
||||||
|
/* Pointer to buffer */
|
||||||
|
const_cast<Scalar *>(src.data()),
|
||||||
|
/* Size of one scalar */
|
||||||
|
sizeof(Scalar),
|
||||||
|
/* Python struct-style format descriptor */
|
||||||
|
format_descriptor<Scalar>::value,
|
||||||
|
/* Number of dimensions */
|
||||||
|
2,
|
||||||
|
/* Buffer dimensions */
|
||||||
|
{ (size_t) src.rows(),
|
||||||
|
(size_t) src.cols() },
|
||||||
|
/* Strides (in bytes) for each index */
|
||||||
|
{ sizeof(Scalar) * (rowMajor ? src.cols() : 1),
|
||||||
|
sizeof(Scalar) * (rowMajor ? 1 : src.rows()) }
|
||||||
|
));
|
||||||
|
return result.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename _T> using cast_op_type = pybind11::detail::cast_op_type<_T>;
|
||||||
|
|
||||||
|
static PYBIND11_DESCR name() {
|
||||||
|
return _("numpy.ndarray[dtype=") + npy_format_descriptor<Scalar>::name() +
|
||||||
|
_(", shape=(") + rows() + _(", ") + cols() + _(")]");
|
||||||
|
}
|
||||||
|
|
||||||
|
operator Type*() { return &value; }
|
||||||
|
operator Type&() { return value; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <typename T = Type, typename std::enable_if<T::RowsAtCompileTime == Eigen::Dynamic, int>::type = 0>
|
||||||
|
static PYBIND11_DESCR rows() { return _("m"); }
|
||||||
|
template <typename T = Type, typename std::enable_if<T::RowsAtCompileTime != Eigen::Dynamic, int>::type = 0>
|
||||||
|
static PYBIND11_DESCR rows() { return _<T::RowsAtCompileTime>(); }
|
||||||
|
template <typename T = Type, typename std::enable_if<T::ColsAtCompileTime == Eigen::Dynamic, int>::type = 0>
|
||||||
|
static PYBIND11_DESCR cols() { return _("n"); }
|
||||||
|
template <typename T = Type, typename std::enable_if<T::ColsAtCompileTime != Eigen::Dynamic, int>::type = 0>
|
||||||
|
static PYBIND11_DESCR cols() { return _<T::ColsAtCompileTime>(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Type value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Type>
|
||||||
|
struct type_caster<Type, typename std::enable_if<is_eigen_sparse<Type>::value>::type> {
|
||||||
|
typedef typename Type::Scalar Scalar;
|
||||||
|
typedef typename std::remove_reference<decltype(*std::declval<Type>().outerIndexPtr())>::type StorageIndex;
|
||||||
|
typedef typename Type::Index Index;
|
||||||
|
static constexpr bool rowMajor = Type::Flags & Eigen::RowMajorBit;
|
||||||
|
|
||||||
|
bool load(handle src, bool) {
|
||||||
|
object obj(src, true);
|
||||||
|
object sparse_module = module::import("scipy.sparse");
|
||||||
|
object matrix_type = sparse_module.attr(
|
||||||
|
rowMajor ? "csr_matrix" : "csc_matrix");
|
||||||
|
|
||||||
|
if (obj.get_type() != matrix_type.ptr()) {
|
||||||
|
try {
|
||||||
|
obj = matrix_type.call(obj);
|
||||||
|
} catch (const error_already_set &) {
|
||||||
|
PyErr_Clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto valuesArray = array_t<Scalar>((object) obj.attr("data"));
|
||||||
|
auto innerIndicesArray = array_t<StorageIndex>((object) obj.attr("indices"));
|
||||||
|
auto outerIndicesArray = array_t<StorageIndex>((object) obj.attr("indptr"));
|
||||||
|
auto shape = pybind11::tuple((pybind11::object) obj.attr("shape"));
|
||||||
|
auto nnz = obj.attr("nnz").cast<Index>();
|
||||||
|
|
||||||
|
if (!valuesArray.check() || !innerIndicesArray.check() ||
|
||||||
|
!outerIndicesArray.check())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
buffer_info outerIndices = outerIndicesArray.request();
|
||||||
|
buffer_info innerIndices = innerIndicesArray.request();
|
||||||
|
buffer_info values = valuesArray.request();
|
||||||
|
|
||||||
|
value = Eigen::MappedSparseMatrix<Scalar, Type::Flags, StorageIndex>(
|
||||||
|
shape[0].cast<Index>(),
|
||||||
|
shape[1].cast<Index>(),
|
||||||
|
nnz,
|
||||||
|
static_cast<StorageIndex *>(outerIndices.ptr),
|
||||||
|
static_cast<StorageIndex *>(innerIndices.ptr),
|
||||||
|
static_cast<Scalar *>(values.ptr)
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static handle cast(const Type *src, return_value_policy policy, handle parent) {
|
||||||
|
return cast(*src, policy, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) {
|
||||||
|
const_cast<Type&>(src).makeCompressed();
|
||||||
|
|
||||||
|
object matrix_type = module::import("scipy.sparse").attr(
|
||||||
|
rowMajor ? "csr_matrix" : "csc_matrix");
|
||||||
|
|
||||||
|
array data(buffer_info(
|
||||||
|
// Pointer to buffer
|
||||||
|
const_cast<Scalar *>(src.valuePtr()),
|
||||||
|
// Size of one scalar
|
||||||
|
sizeof(Scalar),
|
||||||
|
// Python struct-style format descriptor
|
||||||
|
format_descriptor<Scalar>::value,
|
||||||
|
// Number of dimensions
|
||||||
|
1,
|
||||||
|
// Buffer dimensions
|
||||||
|
{ (size_t) src.nonZeros() },
|
||||||
|
// Strides
|
||||||
|
{ sizeof(Scalar) }
|
||||||
|
));
|
||||||
|
|
||||||
|
array outerIndices(buffer_info(
|
||||||
|
// Pointer to buffer
|
||||||
|
const_cast<StorageIndex *>(src.outerIndexPtr()),
|
||||||
|
// Size of one scalar
|
||||||
|
sizeof(StorageIndex),
|
||||||
|
// Python struct-style format descriptor
|
||||||
|
format_descriptor<StorageIndex>::value,
|
||||||
|
// Number of dimensions
|
||||||
|
1,
|
||||||
|
// Buffer dimensions
|
||||||
|
{ (size_t) (rowMajor ? src.rows() : src.cols()) + 1 },
|
||||||
|
// Strides
|
||||||
|
{ sizeof(StorageIndex) }
|
||||||
|
));
|
||||||
|
|
||||||
|
array innerIndices(buffer_info(
|
||||||
|
// Pointer to buffer
|
||||||
|
const_cast<StorageIndex *>(src.innerIndexPtr()),
|
||||||
|
// Size of one scalar
|
||||||
|
sizeof(StorageIndex),
|
||||||
|
// Python struct-style format descriptor
|
||||||
|
format_descriptor<StorageIndex>::value,
|
||||||
|
// Number of dimensions
|
||||||
|
1,
|
||||||
|
// Buffer dimensions
|
||||||
|
{ (size_t) src.nonZeros() },
|
||||||
|
// Strides
|
||||||
|
{ sizeof(StorageIndex) }
|
||||||
|
));
|
||||||
|
|
||||||
|
return matrix_type.call(
|
||||||
|
std::make_tuple(data, innerIndices, outerIndices),
|
||||||
|
std::make_pair(src.rows(), src.cols())
|
||||||
|
).release();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename _T> using cast_op_type = pybind11::detail::cast_op_type<_T>;
|
||||||
|
|
||||||
|
template <typename T = Type, typename std::enable_if<(T::Flags & Eigen::RowMajorBit) != 0, int>::type = 0>
|
||||||
|
static PYBIND11_DESCR name() { return _("scipy.sparse.csr_matrix[dtype=") + npy_format_descriptor<Scalar>::name() + _("]"); }
|
||||||
|
template <typename T = Type, typename std::enable_if<(T::Flags & Eigen::RowMajorBit) == 0, int>::type = 0>
|
||||||
|
static PYBIND11_DESCR name() { return _("scipy.sparse.csc_matrix[dtype=") + npy_format_descriptor<Scalar>::name() + _("]"); }
|
||||||
|
|
||||||
|
operator Type*() { return &value; }
|
||||||
|
operator Type&() { return value; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Type value;
|
||||||
|
};
|
||||||
|
|
||||||
|
NAMESPACE_END(detail)
|
||||||
|
NAMESPACE_END(pybind11)
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
pybind11/numpy.h: Basic NumPy support, auto-vectorization support
|
pybind11/numpy.h: Basic NumPy support, vectorize() wrapper
|
||||||
|
|
||||||
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
|
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
|
||||||
|
|
||||||
@ -20,8 +20,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
NAMESPACE_BEGIN(pybind11)
|
NAMESPACE_BEGIN(pybind11)
|
||||||
|
namespace detail { template <typename type, typename SFINAE = void> struct npy_format_descriptor { }; }
|
||||||
template <typename type, typename SFINAE = void> struct npy_format_descriptor { };
|
|
||||||
|
|
||||||
class array : public buffer {
|
class array : public buffer {
|
||||||
public:
|
public:
|
||||||
@ -84,7 +83,7 @@ public:
|
|||||||
|
|
||||||
template <typename Type> array(size_t size, const Type *ptr) {
|
template <typename Type> array(size_t size, const Type *ptr) {
|
||||||
API& api = lookup_api();
|
API& api = lookup_api();
|
||||||
PyObject *descr = api.PyArray_DescrFromType_(npy_format_descriptor<Type>::value);
|
PyObject *descr = api.PyArray_DescrFromType_(detail::npy_format_descriptor<Type>::value);
|
||||||
if (descr == nullptr)
|
if (descr == nullptr)
|
||||||
pybind11_fail("NumPy: unsupported buffer format!");
|
pybind11_fail("NumPy: unsupported buffer format!");
|
||||||
Py_intptr_t shape = (Py_intptr_t) size;
|
Py_intptr_t shape = (Py_intptr_t) size;
|
||||||
@ -134,7 +133,7 @@ public:
|
|||||||
if (ptr == nullptr)
|
if (ptr == nullptr)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
API &api = lookup_api();
|
API &api = lookup_api();
|
||||||
PyObject *descr = api.PyArray_DescrFromType_(npy_format_descriptor<T>::value);
|
PyObject *descr = api.PyArray_DescrFromType_(detail::npy_format_descriptor<T>::value);
|
||||||
PyObject *result = api.PyArray_FromAny_(
|
PyObject *result = api.PyArray_FromAny_(
|
||||||
ptr, descr, 0, 0,
|
ptr, descr, 0, 0,
|
||||||
API::NPY_ENSURE_ARRAY_ | API::NPY_ARRAY_FORCECAST_ | ExtraFlags,
|
API::NPY_ENSURE_ARRAY_ | API::NPY_ARRAY_FORCECAST_ | ExtraFlags,
|
||||||
@ -144,6 +143,8 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
NAMESPACE_BEGIN(detail)
|
||||||
|
|
||||||
template <typename T> struct npy_format_descriptor<T, typename std::enable_if<std::is_integral<T>::value>::type> {
|
template <typename T> struct npy_format_descriptor<T, typename std::enable_if<std::is_integral<T>::value>::type> {
|
||||||
private:
|
private:
|
||||||
constexpr static const int values[] = {
|
constexpr static const int values[] = {
|
||||||
@ -151,17 +152,21 @@ private:
|
|||||||
array::API::NPY_INT_, array::API::NPY_UINT_, array::API::NPY_LONGLONG_, array::API::NPY_ULONGLONG_ };
|
array::API::NPY_INT_, array::API::NPY_UINT_, array::API::NPY_LONGLONG_, array::API::NPY_ULONGLONG_ };
|
||||||
public:
|
public:
|
||||||
enum { value = values[detail::log2(sizeof(T)) * 2 + (std::is_unsigned<T>::value ? 1 : 0)] };
|
enum { value = values[detail::log2(sizeof(T)) * 2 + (std::is_unsigned<T>::value ? 1 : 0)] };
|
||||||
|
template <typename T2 = T, typename std::enable_if<std::is_signed<T2>::value, int>::type = 0>
|
||||||
|
static PYBIND11_DESCR name() { return _("int") + _<sizeof(T)*8>(); }
|
||||||
|
template <typename T2 = T, typename std::enable_if<!std::is_signed<T2>::value, int>::type = 0>
|
||||||
|
static PYBIND11_DESCR name() { return _("uint") + _<sizeof(T)*8>(); }
|
||||||
};
|
};
|
||||||
template <typename T> constexpr const int npy_format_descriptor<
|
template <typename T> constexpr const int npy_format_descriptor<
|
||||||
T, typename std::enable_if<std::is_integral<T>::value>::type>::values[8];
|
T, typename std::enable_if<std::is_integral<T>::value>::type>::values[8];
|
||||||
|
|
||||||
#define DECL_FMT(t, n) template<> struct npy_format_descriptor<t> { enum { value = array::API::n }; }
|
#define DECL_FMT(Type, NumPyName, Name) template<> struct npy_format_descriptor<Type> { \
|
||||||
DECL_FMT(float, NPY_FLOAT_); DECL_FMT(double, NPY_DOUBLE_); DECL_FMT(bool, NPY_BOOL_);
|
enum { value = array::API::NumPyName }; \
|
||||||
DECL_FMT(std::complex<float>, NPY_CFLOAT_); DECL_FMT(std::complex<double>, NPY_CDOUBLE_);
|
static PYBIND11_DESCR name() { return _(Name); } }
|
||||||
|
DECL_FMT(float, NPY_FLOAT_, "float32"); DECL_FMT(double, NPY_DOUBLE_, "float64"); DECL_FMT(bool, NPY_BOOL_, "bool");
|
||||||
|
DECL_FMT(std::complex<float>, NPY_CFLOAT_, "complex64"); DECL_FMT(std::complex<double>, NPY_CDOUBLE_, "complex128");
|
||||||
#undef DECL_FMT
|
#undef DECL_FMT
|
||||||
|
|
||||||
NAMESPACE_BEGIN(detail)
|
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
using array_iterator = typename std::add_pointer<T>::type;
|
using array_iterator = typename std::add_pointer<T>::type;
|
||||||
|
|
||||||
@ -348,7 +353,7 @@ struct vectorize_helper {
|
|||||||
buffer_info buf = result.request();
|
buffer_info buf = result.request();
|
||||||
Return *output = (Return *) buf.ptr;
|
Return *output = (Return *) buf.ptr;
|
||||||
|
|
||||||
if(trivial_broadcast) {
|
if (trivial_broadcast) {
|
||||||
/* Call the function */
|
/* Call the function */
|
||||||
for (size_t i=0; i<size; ++i) {
|
for (size_t i=0; i<size; ++i) {
|
||||||
output[i] = f((buffers[Index].size == 1
|
output[i] = f((buffers[Index].size == 1
|
||||||
@ -379,7 +384,7 @@ struct vectorize_helper {
|
|||||||
};
|
};
|
||||||
|
|
||||||
template <typename T> struct handle_type_name<array_t<T>> {
|
template <typename T> struct handle_type_name<array_t<T>> {
|
||||||
static PYBIND11_DESCR name() { return _("array[") + type_caster<T>::name() + _("]"); }
|
static PYBIND11_DESCR name() { return _("numpy.ndarray[dtype=") + type_caster<T>::name() + _("]"); }
|
||||||
};
|
};
|
||||||
|
|
||||||
NAMESPACE_END(detail)
|
NAMESPACE_END(detail)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
pybind11/complex.h: Complex number support
|
pybind11/stl.h: Transparent conversion for STL data types
|
||||||
|
|
||||||
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
|
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
|
||||||
|
|
||||||
|
1
setup.py
1
setup.py
@ -20,6 +20,7 @@ setup(
|
|||||||
'include/pybind11/cast.h',
|
'include/pybind11/cast.h',
|
||||||
'include/pybind11/complex.h',
|
'include/pybind11/complex.h',
|
||||||
'include/pybind11/descr.h',
|
'include/pybind11/descr.h',
|
||||||
|
'include/pybind11/eigen.h',
|
||||||
'include/pybind11/numpy.h',
|
'include/pybind11/numpy.h',
|
||||||
'include/pybind11/pybind11.h',
|
'include/pybind11/pybind11.h',
|
||||||
'include/pybind11/stl.h',
|
'include/pybind11/stl.h',
|
||||||
|
81
tools/FindEigen3.cmake
Normal file
81
tools/FindEigen3.cmake
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
# - Try to find Eigen3 lib
|
||||||
|
#
|
||||||
|
# This module supports requiring a minimum version, e.g. you can do
|
||||||
|
# find_package(Eigen3 3.1.2)
|
||||||
|
# to require version 3.1.2 or newer of Eigen3.
|
||||||
|
#
|
||||||
|
# Once done this will define
|
||||||
|
#
|
||||||
|
# EIGEN3_FOUND - system has eigen lib with correct version
|
||||||
|
# EIGEN3_INCLUDE_DIR - the eigen include directory
|
||||||
|
# EIGEN3_VERSION - eigen version
|
||||||
|
|
||||||
|
# Copyright (c) 2006, 2007 Montel Laurent, <montel@kde.org>
|
||||||
|
# Copyright (c) 2008, 2009 Gael Guennebaud, <g.gael@free.fr>
|
||||||
|
# Copyright (c) 2009 Benoit Jacob <jacob.benoit.1@gmail.com>
|
||||||
|
# Redistribution and use is allowed according to the terms of the 2-clause BSD license.
|
||||||
|
|
||||||
|
if(NOT Eigen3_FIND_VERSION)
|
||||||
|
if(NOT Eigen3_FIND_VERSION_MAJOR)
|
||||||
|
set(Eigen3_FIND_VERSION_MAJOR 2)
|
||||||
|
endif(NOT Eigen3_FIND_VERSION_MAJOR)
|
||||||
|
if(NOT Eigen3_FIND_VERSION_MINOR)
|
||||||
|
set(Eigen3_FIND_VERSION_MINOR 91)
|
||||||
|
endif(NOT Eigen3_FIND_VERSION_MINOR)
|
||||||
|
if(NOT Eigen3_FIND_VERSION_PATCH)
|
||||||
|
set(Eigen3_FIND_VERSION_PATCH 0)
|
||||||
|
endif(NOT Eigen3_FIND_VERSION_PATCH)
|
||||||
|
|
||||||
|
set(Eigen3_FIND_VERSION "${Eigen3_FIND_VERSION_MAJOR}.${Eigen3_FIND_VERSION_MINOR}.${Eigen3_FIND_VERSION_PATCH}")
|
||||||
|
endif(NOT Eigen3_FIND_VERSION)
|
||||||
|
|
||||||
|
macro(_eigen3_check_version)
|
||||||
|
file(READ "${EIGEN3_INCLUDE_DIR}/Eigen/src/Core/util/Macros.h" _eigen3_version_header)
|
||||||
|
|
||||||
|
string(REGEX MATCH "define[ \t]+EIGEN_WORLD_VERSION[ \t]+([0-9]+)" _eigen3_world_version_match "${_eigen3_version_header}")
|
||||||
|
set(EIGEN3_WORLD_VERSION "${CMAKE_MATCH_1}")
|
||||||
|
string(REGEX MATCH "define[ \t]+EIGEN_MAJOR_VERSION[ \t]+([0-9]+)" _eigen3_major_version_match "${_eigen3_version_header}")
|
||||||
|
set(EIGEN3_MAJOR_VERSION "${CMAKE_MATCH_1}")
|
||||||
|
string(REGEX MATCH "define[ \t]+EIGEN_MINOR_VERSION[ \t]+([0-9]+)" _eigen3_minor_version_match "${_eigen3_version_header}")
|
||||||
|
set(EIGEN3_MINOR_VERSION "${CMAKE_MATCH_1}")
|
||||||
|
|
||||||
|
set(EIGEN3_VERSION ${EIGEN3_WORLD_VERSION}.${EIGEN3_MAJOR_VERSION}.${EIGEN3_MINOR_VERSION})
|
||||||
|
if(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
|
||||||
|
set(EIGEN3_VERSION_OK FALSE)
|
||||||
|
else(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
|
||||||
|
set(EIGEN3_VERSION_OK TRUE)
|
||||||
|
endif(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
|
||||||
|
|
||||||
|
if(NOT EIGEN3_VERSION_OK)
|
||||||
|
|
||||||
|
message(STATUS "Eigen3 version ${EIGEN3_VERSION} found in ${EIGEN3_INCLUDE_DIR}, "
|
||||||
|
"but at least version ${Eigen3_FIND_VERSION} is required")
|
||||||
|
endif(NOT EIGEN3_VERSION_OK)
|
||||||
|
endmacro(_eigen3_check_version)
|
||||||
|
|
||||||
|
if (EIGEN3_INCLUDE_DIR)
|
||||||
|
|
||||||
|
# in cache already
|
||||||
|
_eigen3_check_version()
|
||||||
|
set(EIGEN3_FOUND ${EIGEN3_VERSION_OK})
|
||||||
|
|
||||||
|
else (EIGEN3_INCLUDE_DIR)
|
||||||
|
|
||||||
|
find_path(EIGEN3_INCLUDE_DIR NAMES signature_of_eigen3_matrix_library
|
||||||
|
PATHS
|
||||||
|
${CMAKE_INSTALL_PREFIX}/include
|
||||||
|
${KDE4_INCLUDE_DIR}
|
||||||
|
PATH_SUFFIXES eigen3 eigen
|
||||||
|
)
|
||||||
|
|
||||||
|
if(EIGEN3_INCLUDE_DIR)
|
||||||
|
_eigen3_check_version()
|
||||||
|
endif(EIGEN3_INCLUDE_DIR)
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(Eigen3 DEFAULT_MSG EIGEN3_INCLUDE_DIR EIGEN3_VERSION_OK)
|
||||||
|
|
||||||
|
mark_as_advanced(EIGEN3_INCLUDE_DIR)
|
||||||
|
|
||||||
|
endif(EIGEN3_INCLUDE_DIR)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user