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")
|
||||
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
|
||||
@ -125,6 +131,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,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
|
||||
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) {
|
||||
typedef Eigen::Stride<Eigen::Dynamic, Eigen::Dynamic> Strides;
|
||||
/* 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();
|
||||
@ -878,21 +1025,46 @@ objects (e.g. a NumPy matrix).
|
||||
throw std::runtime_error("Incompatible buffer dimension!");
|
||||
|
||||
auto strides = Strides(
|
||||
info.strides[Matrix::Flags & Eigen::RowMajorBit ? 0 : 1] / sizeof(Scalar),
|
||||
info.strides[Matrix::Flags & Eigen::RowMajorBit ? 1 : 0] / sizeof(Scalar));
|
||||
info.strides[rowMajor ? 0 : 1] / sizeof(Scalar),
|
||||
info.strides[rowMajor ? 1 : 0] / sizeof(Scalar));
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
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
|
||||
=============
|
||||
@ -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
|
||||
================
|
||||
|
||||
@ -1320,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
|
||||
@ -1372,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
|
||||
@ -1406,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
|
||||
|
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_issues(py::module &);
|
||||
|
||||
#if defined(PYBIND11_TEST_EIGEN)
|
||||
void init_eigen(py::module &);
|
||||
#endif
|
||||
|
||||
PYBIND11_PLUGIN(example) {
|
||||
py::module m("example", "pybind example plugin");
|
||||
|
||||
@ -48,5 +52,9 @@ PYBIND11_PLUGIN(example) {
|
||||
init_ex16(m);
|
||||
init_issues(m);
|
||||
|
||||
#if defined(PYBIND11_TEST_EIGEN)
|
||||
init_eigen(m);
|
||||
#endif
|
||||
|
||||
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>
|
||||
|
||||
@ -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:
|
||||
@ -84,7 +83,7 @@ public:
|
||||
|
||||
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;
|
||||
@ -134,7 +133,7 @@ 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_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> {
|
||||
private:
|
||||
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_ };
|
||||
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;
|
||||
|
||||
@ -379,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