summaryrefslogtreecommitdiff
path: root/ext/pybind11/include/pybind11/eigen.h
diff options
context:
space:
mode:
Diffstat (limited to 'ext/pybind11/include/pybind11/eigen.h')
-rw-r--r--ext/pybind11/include/pybind11/eigen.h117
1 files changed, 69 insertions, 48 deletions
diff --git a/ext/pybind11/include/pybind11/eigen.h b/ext/pybind11/include/pybind11/eigen.h
index 6abe8c48f..a702bf39e 100644
--- a/ext/pybind11/include/pybind11/eigen.h
+++ b/ext/pybind11/include/pybind11/eigen.h
@@ -35,7 +35,7 @@
// of matrices seems highly undesirable.
static_assert(EIGEN_VERSION_AT_LEAST(3,2,7), "Eigen support in pybind11 requires Eigen >= 3.2.7");
-NAMESPACE_BEGIN(pybind11)
+NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
// Provide a convenience alias for easier pass-by-ref usage with fully dynamic strides:
using EigenDStride = Eigen::Stride<Eigen::Dynamic, Eigen::Dynamic>;
@@ -68,16 +68,22 @@ template <typename T> using is_eigen_other = all_of<
template <bool EigenRowMajor> struct EigenConformable {
bool conformable = false;
EigenIndex rows = 0, cols = 0;
- EigenDStride stride{0, 0};
+ EigenDStride stride{0, 0}; // Only valid if negativestrides is false!
+ bool negativestrides = false; // If true, do not use stride!
EigenConformable(bool fits = false) : conformable{fits} {}
// Matrix type:
EigenConformable(EigenIndex r, EigenIndex c,
EigenIndex rstride, EigenIndex cstride) :
- conformable{true}, rows{r}, cols{c},
- stride(EigenRowMajor ? rstride : cstride /* outer stride */,
- EigenRowMajor ? cstride : rstride /* inner stride */)
- {}
+ conformable{true}, rows{r}, cols{c} {
+ // TODO: when Eigen bug #747 is fixed, remove the tests for non-negativity. http://eigen.tuxfamily.org/bz/show_bug.cgi?id=747
+ if (rstride < 0 || cstride < 0) {
+ negativestrides = true;
+ } else {
+ stride = {EigenRowMajor ? rstride : cstride /* outer stride */,
+ EigenRowMajor ? cstride : rstride /* inner stride */ };
+ }
+ }
// Vector type:
EigenConformable(EigenIndex r, EigenIndex c, EigenIndex stride)
: EigenConformable(r, c, r == 1 ? c*stride : stride, c == 1 ? r : r*stride) {}
@@ -86,6 +92,7 @@ template <bool EigenRowMajor> struct EigenConformable {
// To have compatible strides, we need (on both dimensions) one of fully dynamic strides,
// matching strides, or a dimension size of 1 (in which case the stride value is irrelevant)
return
+ !negativestrides &&
(props::inner_stride == Eigen::Dynamic || props::inner_stride == stride.inner() ||
(EigenRowMajor ? cols : rows) == 1) &&
(props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer() ||
@@ -138,8 +145,8 @@ template <typename Type_> struct EigenProps {
EigenIndex
np_rows = a.shape(0),
np_cols = a.shape(1),
- np_rstride = a.strides(0) / sizeof(Scalar),
- np_cstride = a.strides(1) / sizeof(Scalar);
+ np_rstride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar)),
+ np_cstride = a.strides(1) / static_cast<ssize_t>(sizeof(Scalar));
if ((fixed_rows && np_rows != rows) || (fixed_cols && np_cols != cols))
return false;
@@ -149,7 +156,7 @@ template <typename Type_> struct EigenProps {
// Otherwise we're storing an n-vector. Only one of the strides will be used, but whichever
// is used, we want the (single) numpy stride value.
const EigenIndex n = a.shape(0),
- stride = a.strides(0) / sizeof(Scalar);
+ stride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar));
if (vector) { // Eigen type is a compile-time vector
if (fixed && size != n)
@@ -179,39 +186,35 @@ template <typename Type_> struct EigenProps {
constexpr bool show_c_contiguous = show_order && requires_row_major;
constexpr bool show_f_contiguous = !show_c_contiguous && show_order && requires_col_major;
- return _("numpy.ndarray[") + npy_format_descriptor<Scalar>::name() +
- _("[") + _<fixed_rows>(_<(size_t) rows>(), _("m")) +
- _(", ") + _<fixed_cols>(_<(size_t) cols>(), _("n")) +
- _("]") +
- // For a reference type (e.g. Ref<MatrixXd>) we have other constraints that might need to be
- // satisfied: writeable=True (for a mutable reference), and, depending on the map's stride
- // options, possibly f_contiguous or c_contiguous. We include them in the descriptor output
- // to provide some hint as to why a TypeError is occurring (otherwise it can be confusing to
- // see that a function accepts a 'numpy.ndarray[float64[3,2]]' and an error message that you
- // *gave* a numpy.ndarray of the right type and dimensions.
- _<show_writeable>(", flags.writeable", "") +
- _<show_c_contiguous>(", flags.c_contiguous", "") +
- _<show_f_contiguous>(", flags.f_contiguous", "") +
- _("]");
+ return type_descr(_("numpy.ndarray[") + npy_format_descriptor<Scalar>::name() +
+ _("[") + _<fixed_rows>(_<(size_t) rows>(), _("m")) +
+ _(", ") + _<fixed_cols>(_<(size_t) cols>(), _("n")) +
+ _("]") +
+ // For a reference type (e.g. Ref<MatrixXd>) we have other constraints that might need to be
+ // satisfied: writeable=True (for a mutable reference), and, depending on the map's stride
+ // options, possibly f_contiguous or c_contiguous. We include them in the descriptor output
+ // to provide some hint as to why a TypeError is occurring (otherwise it can be confusing to
+ // see that a function accepts a 'numpy.ndarray[float64[3,2]]' and an error message that you
+ // *gave* a numpy.ndarray of the right type and dimensions.
+ _<show_writeable>(", flags.writeable", "") +
+ _<show_c_contiguous>(", flags.c_contiguous", "") +
+ _<show_f_contiguous>(", flags.f_contiguous", "") +
+ _("]")
+ );
}
};
// Casts an Eigen type to numpy array. If given a base, the numpy array references the src data,
// otherwise it'll make a copy. writeable lets you turn off the writeable flag for the array.
template <typename props> handle eigen_array_cast(typename props::Type const &src, handle base = handle(), bool writeable = true) {
- constexpr size_t elem_size = sizeof(typename props::Scalar);
- std::vector<size_t> shape, strides;
- if (props::vector) {
- shape.push_back(src.size());
- strides.push_back(elem_size * src.innerStride());
- }
- else {
- shape.push_back(src.rows());
- shape.push_back(src.cols());
- strides.push_back(elem_size * src.rowStride());
- strides.push_back(elem_size * src.colStride());
- }
- array a(std::move(shape), std::move(strides), src.data(), base);
+ constexpr ssize_t elem_size = sizeof(typename props::Scalar);
+ array a;
+ if (props::vector)
+ a = array({ src.size() }, { elem_size * src.innerStride() }, src.data(), base);
+ else
+ a = array({ src.rows(), src.cols() }, { elem_size * src.rowStride(), elem_size * src.colStride() },
+ src.data(), base);
+
if (!writeable)
array_proxy(a.ptr())->flags &= ~detail::npy_api::NPY_ARRAY_WRITEABLE_;
@@ -246,8 +249,14 @@ struct type_caster<Type, enable_if_t<is_eigen_dense_plain<Type>::value>> {
using Scalar = typename Type::Scalar;
using props = EigenProps<Type>;
- bool load(handle src, bool) {
- auto buf = array_t<Scalar>::ensure(src);
+ bool load(handle src, bool convert) {
+ // If we're in no-convert mode, only load if given an array of the correct type
+ if (!convert && !isinstance<array_t<Scalar>>(src))
+ return false;
+
+ // Coerce into an array, but don't do type conversion yet; the copy below handles it.
+ auto buf = array::ensure(src);
+
if (!buf)
return false;
@@ -257,9 +266,19 @@ struct type_caster<Type, enable_if_t<is_eigen_dense_plain<Type>::value>> {
auto fits = props::conformable(buf);
if (!fits)
- return false; // Non-comformable vector/matrix types
+ return false;
+
+ // Allocate the new type, then build a numpy reference into it
+ value = Type(fits.rows, fits.cols);
+ auto ref = reinterpret_steal<array>(eigen_ref_array<props>(value));
+ if (dims == 1) ref = ref.squeeze();
- value = Eigen::Map<const Type, 0, EigenDStride>(buf.data(), fits.rows, fits.cols, fits.stride);
+ int result = detail::npy_api::get().PyArray_CopyInto_(ref.ptr(), buf.ptr());
+
+ if (result < 0) { // Copy failed!
+ PyErr_Clear();
+ return false;
+ }
return true;
}
@@ -318,11 +337,12 @@ public:
return cast_impl(src, policy, parent);
}
- static PYBIND11_DESCR name() { return type_descr(props::descriptor()); }
+ static PYBIND11_DESCR name() { return props::descriptor(); }
operator Type*() { return &value; }
operator Type&() { return value; }
- template <typename T> using cast_op_type = cast_op_type<T>;
+ operator Type&&() && { return std::move(value); }
+ template <typename T> using cast_op_type = movable_cast_op_type<T>;
private:
Type value;
@@ -442,6 +462,7 @@ public:
if (!fits || !fits.template stride_compatible<props>())
return false;
copy_or_ref = std::move(copy);
+ loader_life_support::add_patient(copy_or_ref);
}
ref.reset();
@@ -522,7 +543,7 @@ public:
template<typename Type>
struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
typedef typename Type::Scalar Scalar;
- typedef typename std::remove_reference<decltype(*std::declval<Type>().outerIndexPtr())>::type StorageIndex;
+ typedef remove_reference_t<decltype(*std::declval<Type>().outerIndexPtr())> StorageIndex;
typedef typename Type::Index Index;
static constexpr bool rowMajor = Type::IsRowMajor;
@@ -535,7 +556,7 @@ struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
object matrix_type = sparse_module.attr(
rowMajor ? "csr_matrix" : "csc_matrix");
- if (obj.get_type() != matrix_type.ptr()) {
+ if (!obj.get_type().is(matrix_type)) {
try {
obj = matrix_type(obj);
} catch (const error_already_set &) {
@@ -565,9 +586,9 @@ struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
object matrix_type = module::import("scipy.sparse").attr(
rowMajor ? "csr_matrix" : "csc_matrix");
- array data((size_t) src.nonZeros(), src.valuePtr());
- array outerIndices((size_t) (rowMajor ? src.rows() : src.cols()) + 1, src.outerIndexPtr());
- array innerIndices((size_t) src.nonZeros(), src.innerIndexPtr());
+ array data(src.nonZeros(), src.valuePtr());
+ array outerIndices((rowMajor ? src.rows() : src.cols()) + 1, src.outerIndexPtr());
+ array innerIndices(src.nonZeros(), src.innerIndexPtr());
return matrix_type(
std::make_tuple(data, innerIndices, outerIndices),
@@ -580,7 +601,7 @@ struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
};
NAMESPACE_END(detail)
-NAMESPACE_END(pybind11)
+NAMESPACE_END(PYBIND11_NAMESPACE)
#if defined(__GNUG__) || defined(__clang__)
# pragma GCC diagnostic pop