mirror of
https://github.com/pybind/pybind11.git
synced 2025-02-16 21:57:55 +00:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
587fc20b68
@ -28,7 +28,7 @@ string(TOUPPER "${CMAKE_BUILD_TYPE}" U_CMAKE_BUILD_TYPE)
|
||||
set(Python_ADDITIONAL_VERSIONS 3.4 3.5 3.6 3.7)
|
||||
if (NOT ${PYBIND11_PYTHON_VERSION} STREQUAL "")
|
||||
find_package(PythonLibs ${PYBIND11_PYTHON_VERSION} EXACT)
|
||||
if (NOT PythonLibs_FOUND)
|
||||
if (NOT PYTHONLIBS_FOUND)
|
||||
find_package(PythonLibs ${PYBIND11_PYTHON_VERSION} REQUIRED)
|
||||
endif()
|
||||
else()
|
||||
@ -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")
|
||||
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_directories(include)
|
||||
|
||||
@ -96,6 +101,7 @@ set(PYBIND11_HEADERS
|
||||
include/pybind11/common.h
|
||||
include/pybind11/complex.h
|
||||
include/pybind11/descr.h
|
||||
include/pybind11/eigen.h
|
||||
include/pybind11/functional.h
|
||||
include/pybind11/numpy.h
|
||||
include/pybind11/operators.h
|
||||
@ -126,6 +132,15 @@ set(PYBIND11_EXAMPLES
|
||||
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
|
||||
add_library(example SHARED
|
||||
${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
|
||||
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
|
||||
===============
|
||||
|
||||
Python supports an extremely general and convenient approach for exchanging
|
||||
data between plugin libraries. Types can expose a buffer view [#f1]_,
|
||||
which provides fast direct access to the raw internal representation. Suppose
|
||||
we want to bind the following simplistic Matrix class:
|
||||
data between plugin libraries. Types can expose a buffer view [#f2]_, which
|
||||
provides fast direct access to the raw internal data representation. Suppose we
|
||||
want to bind the following simplistic Matrix class:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
@ -856,41 +1000,71 @@ 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
|
||||
define a custom constructor for the Eigen double precision matrix
|
||||
(``Eigen::MatrixXd``) type, which supports initialization from compatible
|
||||
buffer
|
||||
objects (e.g. a NumPy matrix).
|
||||
buffer objects (e.g. a NumPy matrix).
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
py::class_<Eigen::MatrixXd>(m, "MatrixXd")
|
||||
.def("__init__", [](Eigen::MatrixXd &m, py::buffer b) {
|
||||
/* Bind MatrixXd (or some other Eigen type) to Python */
|
||||
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;
|
||||
|
||||
/* Request a buffer descriptor from Python */
|
||||
py::buffer_info info = b.request();
|
||||
|
||||
/* Some sanity checks ... */
|
||||
if (info.format != py::format_descriptor<double>::value)
|
||||
if (info.format != py::format_descriptor<Scalar>::value)
|
||||
throw std::runtime_error("Incompatible format: expected a double array!");
|
||||
|
||||
if (info.ndim != 2)
|
||||
throw std::runtime_error("Incompatible buffer dimension!");
|
||||
|
||||
if (info.strides[0] == sizeof(double)) {
|
||||
/* Buffer has the right layout -- directly copy. */
|
||||
new (&m) Eigen::MatrixXd(info.shape[0], info.shape[1]);
|
||||
memcpy(m.data(), info.ptr, sizeof(double) * m.size());
|
||||
} else {
|
||||
/* Oops -- the buffer is transposed */
|
||||
new (&m) Eigen::MatrixXd(info.shape[1], info.shape[0]);
|
||||
memcpy(m.data(), info.ptr, sizeof(double) * m.size());
|
||||
m.transposeInPlace();
|
||||
}
|
||||
auto strides = Strides(
|
||||
info.strides[rowMajor ? 0 : 1] / sizeof(Scalar),
|
||||
info.strides[rowMajor ? 1 : 0] / sizeof(Scalar));
|
||||
|
||||
auto map = Eigen::Map<Matrix, 0, Strides>(
|
||||
static_cat<Scalar *>(info.ptr), info.shape[0], info.shape[1], strides);
|
||||
|
||||
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::
|
||||
|
||||
The file :file:`example/example7.cpp` contains a complete example that
|
||||
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
|
||||
=============
|
||||
@ -902,16 +1076,30 @@ type of Python object satisfying the buffer protocol).
|
||||
In many situations, we want to define a function which only accepts a NumPy
|
||||
array of a certain data type. This is possible via the ``py::array_t<T>``
|
||||
template. For instance, the following function requires the argument to be a
|
||||
dense array of doubles in C-style ordering.
|
||||
NumPy array containing double precision values.
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
void f(py::array_t<double> array);
|
||||
|
||||
When it is invoked with a different type (e.g. an integer), the binding code
|
||||
will attempt to cast the input into a NumPy array of the requested type. Note
|
||||
that this feature requires the :file:``pybind11/numpy.h`` header to be
|
||||
included.
|
||||
When it is invoked with a different type (e.g. an integer or a list of
|
||||
integers), the binding code will attempt to cast the input into a NumPy array
|
||||
of the requested type. Note that this feature requires the
|
||||
:file:``pybind11/numpy.h`` header to be included.
|
||||
|
||||
Data in NumPy arrays is not guaranteed to packed in a dense manner;
|
||||
furthermore, entries can be separated by arbitrary column and row strides.
|
||||
Sometimes, it can be useful to require a function to only accept dense arrays
|
||||
using either the C (row-major) or Fortran (column-major) ordering. This can be
|
||||
accomplished via a second template argument with values ``py::array::c_style``
|
||||
or ``py::array::f_style``.
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
void f(py::array_t<double, py::array::c_style> array);
|
||||
|
||||
As before, the implementation will attempt to convert non-conforming arguments
|
||||
into an array satisfying the specified requirements.
|
||||
|
||||
Vectorizing functions
|
||||
=====================
|
||||
@ -1183,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
|
||||
================
|
||||
|
||||
@ -1304,7 +1393,7 @@ Suppose the class in question has the following signature:
|
||||
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:
|
||||
|
||||
.. code-block:: cpp
|
||||
@ -1356,14 +1445,14 @@ memory corruption and/or segmentation faults.
|
||||
The file :file:`example/example15.cpp` contains a complete example that
|
||||
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
|
||||
=====================================
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
There are two potential gotchas when using this approach: first, make sure that
|
||||
@ -1390,6 +1479,6 @@ work, it is important that all lines are indented consistently, i.e.:
|
||||
----------
|
||||
)mydelimiter");
|
||||
|
||||
.. [#f3] http://www.sphinx-doc.org
|
||||
.. [#f4] http://github.com/pybind/pbtest
|
||||
.. [#f4] http://www.sphinx-doc.org
|
||||
.. [#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` |
|
||||
+---------------------------------+--------------------------+-------------------------------+
|
||||
| ``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
|
||||
|
@ -5,6 +5,7 @@ Changelog
|
||||
|
||||
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
|
||||
pointer
|
||||
* Don't allow casting a ``None`` value into a C++ lvalue reference
|
||||
|
@ -48,7 +48,7 @@ subdirectory named :file:`pybind11`.
|
||||
set(Python_ADDITIONAL_VERSIONS 3.4 3.5 3.6 3.7)
|
||||
if (NOT ${EXAMPLE_PYTHON_VERSION} STREQUAL "")
|
||||
find_package(PythonLibs ${EXAMPLE_PYTHON_VERSION} EXACT)
|
||||
if (NOT PythonLibs_FOUND)
|
||||
if (NOT PYTHONLIBS_FOUND)
|
||||
find_package(PythonLibs ${EXAMPLE_PYTHON_VERSION} REQUIRED)
|
||||
endif()
|
||||
else()
|
||||
|
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
|
@ -28,6 +28,10 @@ void init_ex16(py::module &);
|
||||
void init_ex17(py::module &);
|
||||
void init_issues(py::module &);
|
||||
|
||||
#if defined(PYBIND11_TEST_EIGEN)
|
||||
void init_eigen(py::module &);
|
||||
#endif
|
||||
|
||||
PYBIND11_PLUGIN(example) {
|
||||
py::module m("example", "pybind example plugin");
|
||||
|
||||
@ -50,5 +54,9 @@ PYBIND11_PLUGIN(example) {
|
||||
init_ex17(m);
|
||||
init_issues(m);
|
||||
|
||||
#if defined(PYBIND11_TEST_EIGEN)
|
||||
init_eigen(m);
|
||||
#endif
|
||||
|
||||
return m.ptr();
|
||||
}
|
||||
|
@ -227,7 +227,7 @@ private:
|
||||
|
||||
NAMESPACE_BEGIN(detail)
|
||||
|
||||
static constexpr int log2(size_t n, int k = 0) { return (n <= 1) ? k : log2(n >> 1, k + 1); }
|
||||
inline static constexpr int log2(size_t n, int k = 0) { return (n <= 1) ? k : log2(n >> 1, k + 1); }
|
||||
|
||||
inline std::string error_string();
|
||||
|
||||
|
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>
|
||||
|
||||
@ -20,8 +20,7 @@
|
||||
#endif
|
||||
|
||||
NAMESPACE_BEGIN(pybind11)
|
||||
|
||||
template <typename type, typename SFINAE = void> struct npy_format_descriptor { };
|
||||
namespace detail { template <typename type, typename SFINAE = void> struct npy_format_descriptor { }; }
|
||||
|
||||
class array : public buffer {
|
||||
public:
|
||||
@ -77,9 +76,14 @@ public:
|
||||
|
||||
PYBIND11_OBJECT_DEFAULT(array, buffer, lookup_api().PyArray_Check_)
|
||||
|
||||
enum {
|
||||
c_style = API::NPY_C_CONTIGUOUS_,
|
||||
f_style = API::NPY_F_CONTIGUOUS_
|
||||
};
|
||||
|
||||
template <typename Type> array(size_t size, const Type *ptr) {
|
||||
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)
|
||||
pybind11_fail("NumPy: unsupported buffer format!");
|
||||
Py_intptr_t shape = (Py_intptr_t) size;
|
||||
@ -120,7 +124,7 @@ protected:
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> class array_t : public array {
|
||||
template <typename T, int ExtraFlags = 0> class array_t : public array {
|
||||
public:
|
||||
PYBIND11_OBJECT_CVT(array_t, array, is_non_null, m_ptr = ensure(m_ptr));
|
||||
array_t() : array() { }
|
||||
@ -129,15 +133,18 @@ public:
|
||||
if (ptr == nullptr)
|
||||
return nullptr;
|
||||
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_(
|
||||
ptr, descr, 0, 0, API::NPY_C_CONTIGUOUS_ | API::NPY_ENSURE_ARRAY_
|
||||
| API::NPY_ARRAY_FORCECAST_, nullptr);
|
||||
ptr, descr, 0, 0,
|
||||
API::NPY_ENSURE_ARRAY_ | API::NPY_ARRAY_FORCECAST_ | ExtraFlags,
|
||||
nullptr);
|
||||
Py_DECREF(ptr);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
NAMESPACE_BEGIN(detail)
|
||||
|
||||
template <typename T> struct npy_format_descriptor<T, typename std::enable_if<std::is_integral<T>::value>::type> {
|
||||
private:
|
||||
constexpr static const int values[] = {
|
||||
@ -145,17 +152,21 @@ private:
|
||||
array::API::NPY_INT_, array::API::NPY_UINT_, array::API::NPY_LONGLONG_, array::API::NPY_ULONGLONG_ };
|
||||
public:
|
||||
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<
|
||||
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 }; }
|
||||
DECL_FMT(float, NPY_FLOAT_); DECL_FMT(double, NPY_DOUBLE_); DECL_FMT(bool, NPY_BOOL_);
|
||||
DECL_FMT(std::complex<float>, NPY_CFLOAT_); DECL_FMT(std::complex<double>, NPY_CDOUBLE_);
|
||||
#define DECL_FMT(Type, NumPyName, Name) template<> struct npy_format_descriptor<Type> { \
|
||||
enum { value = array::API::NumPyName }; \
|
||||
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
|
||||
|
||||
NAMESPACE_BEGIN(detail)
|
||||
|
||||
template <class T>
|
||||
using array_iterator = typename std::add_pointer<T>::type;
|
||||
|
||||
@ -342,7 +353,7 @@ struct vectorize_helper {
|
||||
buffer_info buf = result.request();
|
||||
Return *output = (Return *) buf.ptr;
|
||||
|
||||
if(trivial_broadcast) {
|
||||
if (trivial_broadcast) {
|
||||
/* Call the function */
|
||||
for (size_t i=0; i<size; ++i) {
|
||||
output[i] = f((buffers[Index].size == 1
|
||||
@ -373,7 +384,7 @@ struct vectorize_helper {
|
||||
};
|
||||
|
||||
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)
|
||||
|
@ -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>
|
||||
|
||||
|
1
setup.py
1
setup.py
@ -20,6 +20,7 @@ setup(
|
||||
'include/pybind11/cast.h',
|
||||
'include/pybind11/complex.h',
|
||||
'include/pybind11/descr.h',
|
||||
'include/pybind11/eigen.h',
|
||||
'include/pybind11/numpy.h',
|
||||
'include/pybind11/pybind11.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