Commit 9e0a0568 by Wenzel Jakob

transparent conversion of dense and sparse Eigen types

parent 9ac5bc55
......@@ -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}
......
......@@ -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
......
/*
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;
});
}
#!/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())))
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();
}
/*
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
/*
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;
......@@ -348,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
......@@ -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)
......
/*
pybind11/complex.h: Complex number support
pybind11/stl.h: Transparent conversion for STL data types
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
......
......@@ -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',
......
# - 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)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment