μHAL (v2.8.17)
Part of the IPbus software repository
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
tensor.h
Go to the documentation of this file.
1/*
2 pybind11/eigen/tensor.h: Transparent conversion for Eigen tensors
3
4 All rights reserved. Use of this source code is governed by a
5 BSD-style license that can be found in the LICENSE file.
6*/
7
8#pragma once
9
10#include "../numpy.h"
11
12#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
13static_assert(__GNUC__ > 5, "Eigen Tensor support in pybind11 requires GCC > 5.0");
14#endif
15
16// Disable warnings for Eigen
17PYBIND11_WARNING_PUSH
20#if defined(__MINGW32__)
21PYBIND11_WARNING_DISABLE_GCC("-Wmaybe-uninitialized")
22#endif
23
24#include <unsupported/Eigen/CXX11/Tensor>
25
27
28static_assert(EIGEN_VERSION_AT_LEAST(3, 3, 0),
29 "Eigen Tensor support in pybind11 requires Eigen >= 3.3.0");
30
32
34
36
37inline bool is_tensor_aligned(const void *data) {
38 return (reinterpret_cast<std::size_t>(data) % EIGEN_DEFAULT_ALIGN_BYTES) == 0;
39}
40
41template <typename T>
43 static_assert((static_cast<int>(T::Layout) == static_cast<int>(Eigen::RowMajor))
44 || (static_cast<int>(T::Layout) == static_cast<int>(Eigen::ColMajor)),
45 "Layout must be row or column major");
46 return (static_cast<int>(T::Layout) == static_cast<int>(Eigen::RowMajor)) ? array::c_style
48}
49
50template <typename T>
52
53template <typename Scalar_, int NumIndices_, int Options_, typename IndexType>
54struct eigen_tensor_helper<Eigen::Tensor<Scalar_, NumIndices_, Options_, IndexType>> {
55 using Type = Eigen::Tensor<Scalar_, NumIndices_, Options_, IndexType>;
56 using ValidType = void;
57
58 static Eigen::DSizes<typename Type::Index, Type::NumIndices> get_shape(const Type &f) {
59 return f.dimensions();
60 }
61
62 static constexpr bool
63 is_correct_shape(const Eigen::DSizes<typename Type::Index, Type::NumIndices> & /*shape*/) {
64 return true;
65 }
66
67 template <typename T>
68 struct helper {};
69
70 template <size_t... Is>
71 struct helper<index_sequence<Is...>> {
72 static constexpr auto value = concat(const_name(((void) Is, "?"))...);
73 };
74
75 static constexpr auto dimensions_descriptor
76 = helper<decltype(make_index_sequence<Type::NumIndices>())>::value;
77
78 template <typename... Args>
79 static Type *alloc(Args &&...args) {
80 return new Type(std::forward<Args>(args)...);
81 }
82
83 static void free(Type *tensor) { delete tensor; }
84};
85
86template <typename Scalar_, typename std::ptrdiff_t... Indices, int Options_, typename IndexType>
88 Eigen::TensorFixedSize<Scalar_, Eigen::Sizes<Indices...>, Options_, IndexType>> {
89 using Type = Eigen::TensorFixedSize<Scalar_, Eigen::Sizes<Indices...>, Options_, IndexType>;
90 using ValidType = void;
91
92 static constexpr Eigen::DSizes<typename Type::Index, Type::NumIndices>
93 get_shape(const Type & /*f*/) {
94 return get_shape();
95 }
96
97 static constexpr Eigen::DSizes<typename Type::Index, Type::NumIndices> get_shape() {
98 return Eigen::DSizes<typename Type::Index, Type::NumIndices>(Indices...);
99 }
100
101 static bool
102 is_correct_shape(const Eigen::DSizes<typename Type::Index, Type::NumIndices> &shape) {
103 return get_shape() == shape;
104 }
105
106 static constexpr auto dimensions_descriptor = concat(const_name<Indices>()...);
107
108 template <typename... Args>
109 static Type *alloc(Args &&...args) {
110 Eigen::aligned_allocator<Type> allocator;
111 return ::new (allocator.allocate(1)) Type(std::forward<Args>(args)...);
112 }
113
114 static void free(Type *tensor) {
115 Eigen::aligned_allocator<Type> allocator;
116 tensor->~Type();
117 allocator.deallocate(tensor, 1);
118 }
119};
120
121template <typename Type, bool ShowDetails, bool NeedsWriteable = false>
123 static constexpr auto details
124 = const_name<NeedsWriteable>(", flags.writeable", "")
125 + const_name<static_cast<int>(Type::Layout) == static_cast<int>(Eigen::RowMajor)>(
126 ", flags.c_contiguous", ", flags.f_contiguous");
127 static constexpr auto value
129 + const_name("[") + eigen_tensor_helper<remove_cv_t<Type>>::dimensions_descriptor
130 + const_name("]") + const_name<ShowDetails>(details, const_name("")) + const_name("]");
131};
132
133// When EIGEN_AVOID_STL_ARRAY is defined, Eigen::DSizes<T, 0> does not have the begin() member
134// function. Falling back to a simple loop works around this issue.
135//
136// We need to disable the type-limits warning for the inner loop when size = 0.
137
138PYBIND11_WARNING_PUSH
139PYBIND11_WARNING_DISABLE_GCC("-Wtype-limits")
140
141template <typename T, int size>
142std::vector<T> convert_dsizes_to_vector(const Eigen::DSizes<T, size> &arr) {
143 std::vector<T> result(size);
144
145 for (size_t i = 0; i < size; i++) {
146 result[i] = arr[i];
147 }
148
149 return result;
150}
151
152template <typename T, int size>
153Eigen::DSizes<T, size> get_shape_for_array(const array &arr) {
154 Eigen::DSizes<T, size> result;
155 const T *shape = arr.shape();
156 for (size_t i = 0; i < size; i++) {
157 result[i] = shape[i];
158 }
159
160 return result;
161}
162
164
165template <typename Type>
166struct type_caster<Type, typename eigen_tensor_helper<Type>::ValidType> {
168 static constexpr auto temp_name = get_tensor_descriptor<Type, false>::value;
169 PYBIND11_TYPE_CASTER(Type, temp_name);
170
171 bool load(handle src, bool convert) {
172 if (!convert) {
173 if (!isinstance<array>(src)) {
174 return false;
175 }
176 array temp = array::ensure(src);
177 if (!temp) {
178 return false;
179 }
180
181 if (!temp.dtype().is(dtype::of<typename Type::Scalar>())) {
182 return false;
183 }
184 }
185
187 reinterpret_borrow<object>(src));
188
189 if (arr.ndim() != Type::NumIndices) {
190 return false;
191 }
192 auto shape = get_shape_for_array<typename Type::Index, Type::NumIndices>(arr);
193
194 if (!Helper::is_correct_shape(shape)) {
195 return false;
196 }
197
198#if EIGEN_VERSION_AT_LEAST(3, 4, 0)
199 auto data_pointer = arr.data();
200#else
201 // Handle Eigen bug
202 auto data_pointer = const_cast<typename Type::Scalar *>(arr.data());
203#endif
204
205 if (is_tensor_aligned(arr.data())) {
206 value = Eigen::TensorMap<const Type, Eigen::Aligned>(data_pointer, shape);
207 } else {
208 value = Eigen::TensorMap<const Type>(data_pointer, shape);
209 }
210
211 return true;
212 }
213
214 static handle cast(Type &&src, return_value_policy policy, handle parent) {
215 if (policy == return_value_policy::reference
216 || policy == return_value_policy::reference_internal) {
217 pybind11_fail("Cannot use a reference return value policy for an rvalue");
218 }
219 return cast_impl(&src, return_value_policy::move, parent);
220 }
221
222 static handle cast(const Type &&src, return_value_policy policy, handle parent) {
223 if (policy == return_value_policy::reference
224 || policy == return_value_policy::reference_internal) {
225 pybind11_fail("Cannot use a reference return value policy for an rvalue");
226 }
227 return cast_impl(&src, return_value_policy::move, parent);
228 }
229
230 static handle cast(Type &src, return_value_policy policy, handle parent) {
231 if (policy == return_value_policy::automatic
232 || policy == return_value_policy::automatic_reference) {
233 policy = return_value_policy::copy;
234 }
235 return cast_impl(&src, policy, parent);
236 }
237
238 static handle cast(const Type &src, return_value_policy policy, handle parent) {
239 if (policy == return_value_policy::automatic
240 || policy == return_value_policy::automatic_reference) {
241 policy = return_value_policy::copy;
242 }
243 return cast(&src, policy, parent);
244 }
245
246 static handle cast(Type *src, return_value_policy policy, handle parent) {
247 if (policy == return_value_policy::automatic) {
248 policy = return_value_policy::take_ownership;
249 } else if (policy == return_value_policy::automatic_reference) {
250 policy = return_value_policy::reference;
251 }
252 return cast_impl(src, policy, parent);
253 }
254
255 static handle cast(const Type *src, return_value_policy policy, handle parent) {
256 if (policy == return_value_policy::automatic) {
257 policy = return_value_policy::take_ownership;
258 } else if (policy == return_value_policy::automatic_reference) {
259 policy = return_value_policy::reference;
260 }
261 return cast_impl(src, policy, parent);
262 }
263
264 template <typename C>
265 static handle cast_impl(C *src, return_value_policy policy, handle parent) {
266 object parent_object;
267 bool writeable = false;
268 switch (policy) {
269 case return_value_policy::move:
270 if (std::is_const<C>::value) {
271 pybind11_fail("Cannot move from a constant reference");
272 }
273
274 src = Helper::alloc(std::move(*src));
275
276 parent_object
277 = capsule(src, [](void *ptr) { Helper::free(reinterpret_cast<Type *>(ptr)); });
278 writeable = true;
279 break;
280
281 case return_value_policy::take_ownership:
282 if (std::is_const<C>::value) {
283 // This cast is ugly, and might be UB in some cases, but we don't have an
284 // alternative here as we must free that memory
285 Helper::free(const_cast<Type *>(src));
286 pybind11_fail("Cannot take ownership of a const reference");
287 }
288
289 parent_object
290 = capsule(src, [](void *ptr) { Helper::free(reinterpret_cast<Type *>(ptr)); });
291 writeable = true;
292 break;
293
294 case return_value_policy::copy:
295 writeable = true;
296 break;
297
298 case return_value_policy::reference:
299 parent_object = none();
300 writeable = !std::is_const<C>::value;
301 break;
302
303 case return_value_policy::reference_internal:
304 // Default should do the right thing
305 if (!parent) {
306 pybind11_fail("Cannot use reference internal when there is no parent");
307 }
308 parent_object = reinterpret_borrow<object>(parent);
309 writeable = !std::is_const<C>::value;
310 break;
311
312 default:
313 pybind11_fail("pybind11 bug in eigen.h, please file a bug report");
314 }
315
317 convert_dsizes_to_vector(Helper::get_shape(*src)), src->data(), parent_object);
318
319 if (!writeable) {
320 array_proxy(result.ptr())->flags &= ~detail::npy_api::NPY_ARRAY_WRITEABLE_;
321 }
322
323 return result.release();
324 }
325};
326
327template <typename StoragePointerType,
328 bool needs_writeable,
330StoragePointerType get_array_data_for_type(array &arr) {
331#if EIGEN_VERSION_AT_LEAST(3, 4, 0)
332 return reinterpret_cast<StoragePointerType>(arr.data());
333#else
334 // Handle Eigen bug
335 return reinterpret_cast<StoragePointerType>(const_cast<void *>(arr.data()));
336#endif
337}
338
339template <typename StoragePointerType,
340 bool needs_writeable,
342StoragePointerType get_array_data_for_type(array &arr) {
343 return reinterpret_cast<StoragePointerType>(arr.mutable_data());
344}
345
346template <typename T, typename = void>
348
349template <typename MapType>
350struct get_storage_pointer_type<MapType, void_t<typename MapType::StoragePointerType>> {
351 using SPT = typename MapType::StoragePointerType;
352};
353
354template <typename MapType>
355struct get_storage_pointer_type<MapType, void_t<typename MapType::PointerArgType>> {
356 using SPT = typename MapType::PointerArgType;
357};
358
359template <typename Type, int Options>
360struct type_caster<Eigen::TensorMap<Type, Options>,
361 typename eigen_tensor_helper<remove_cv_t<Type>>::ValidType> {
362 using MapType = Eigen::TensorMap<Type, Options>;
364
365 bool load(handle src, bool /*convert*/) {
366 // Note that we have a lot more checks here as we want to make sure to avoid copies
367 if (!isinstance<array>(src)) {
368 return false;
369 }
370 auto arr = reinterpret_borrow<array>(src);
371 if ((arr.flags() & compute_array_flag_from_tensor<Type>()) == 0) {
372 return false;
373 }
374
375 if (!arr.dtype().is(dtype::of<typename Type::Scalar>())) {
376 return false;
377 }
378
379 if (arr.ndim() != Type::NumIndices) {
380 return false;
381 }
382
383 constexpr bool is_aligned = (Options & Eigen::Aligned) != 0;
384
385 if (is_aligned && !is_tensor_aligned(arr.data())) {
386 return false;
387 }
388
389 auto shape = get_shape_for_array<typename Type::Index, Type::NumIndices>(arr);
390
391 if (!Helper::is_correct_shape(shape)) {
392 return false;
393 }
394
395 if (needs_writeable && !arr.writeable()) {
396 return false;
397 }
398
399 auto result = get_array_data_for_type<typename get_storage_pointer_type<MapType>::SPT,
400 needs_writeable>(arr);
401
402 value.reset(new MapType(std::move(result), std::move(shape)));
403
404 return true;
405 }
406
407 static handle cast(MapType &&src, return_value_policy policy, handle parent) {
408 return cast_impl(&src, policy, parent);
409 }
410
411 static handle cast(const MapType &&src, return_value_policy policy, handle parent) {
412 return cast_impl(&src, policy, parent);
413 }
414
415 static handle cast(MapType &src, return_value_policy policy, handle parent) {
416 if (policy == return_value_policy::automatic
417 || policy == return_value_policy::automatic_reference) {
418 policy = return_value_policy::copy;
419 }
420 return cast_impl(&src, policy, parent);
421 }
422
423 static handle cast(const MapType &src, return_value_policy policy, handle parent) {
424 if (policy == return_value_policy::automatic
425 || policy == return_value_policy::automatic_reference) {
426 policy = return_value_policy::copy;
427 }
428 return cast(&src, policy, parent);
429 }
430
431 static handle cast(MapType *src, return_value_policy policy, handle parent) {
432 if (policy == return_value_policy::automatic) {
433 policy = return_value_policy::take_ownership;
434 } else if (policy == return_value_policy::automatic_reference) {
435 policy = return_value_policy::reference;
436 }
437 return cast_impl(src, policy, parent);
438 }
439
440 static handle cast(const MapType *src, return_value_policy policy, handle parent) {
441 if (policy == return_value_policy::automatic) {
442 policy = return_value_policy::take_ownership;
443 } else if (policy == return_value_policy::automatic_reference) {
444 policy = return_value_policy::reference;
445 }
446 return cast_impl(src, policy, parent);
447 }
448
449 template <typename C>
450 static handle cast_impl(C *src, return_value_policy policy, handle parent) {
451 object parent_object;
452 constexpr bool writeable = !std::is_const<C>::value;
453 switch (policy) {
454 case return_value_policy::reference:
455 parent_object = none();
456 break;
457
458 case return_value_policy::reference_internal:
459 // Default should do the right thing
460 if (!parent) {
461 pybind11_fail("Cannot use reference internal when there is no parent");
462 }
463 parent_object = reinterpret_borrow<object>(parent);
464 break;
465
466 case return_value_policy::take_ownership:
467 delete src;
468 // fallthrough
469 default:
470 // move, take_ownership don't make any sense for a ref/map:
471 pybind11_fail("Invalid return_value_policy for Eigen Map type, must be either "
472 "reference or reference_internal");
473 }
474
476 convert_dsizes_to_vector(Helper::get_shape(*src)),
477 src->data(),
478 std::move(parent_object));
479
480 if (!writeable) {
481 array_proxy(result.ptr())->flags &= ~detail::npy_api::NPY_ARRAY_WRITEABLE_;
482 }
483
484 return result.release();
485 }
486
487#if EIGEN_VERSION_AT_LEAST(3, 4, 0)
488
489 static constexpr bool needs_writeable = !std::is_const<typename std::remove_pointer<
491#else
492 // Handle Eigen bug
493 static constexpr bool needs_writeable = !std::is_const<Type>::value;
494#endif
495
496protected:
497 // TODO: Move to std::optional once std::optional has more support
498 std::unique_ptr<MapType> value;
499
500public:
502 explicit operator MapType *() { return value.get(); }
503 explicit operator MapType &() { return *value; }
504 explicit operator MapType &&() && { return std::move(*value); }
505
506 template <typename T_>
507 using cast_op_type = ::pybind11::detail::movable_cast_op_type<T_>;
508};
509
Definition: pytypes.h:1776
Definition: numpy.h:991
Definition: numpy.h:655
pybind11::dtype dtype() const
Array descriptor (dtype)
Definition: numpy.h:752
static array ensure(handle h, int ExtraFlags=0)
Ensure that the argument is a NumPy array In case of an error, nullptr is returned and the Python err...
Definition: numpy.h:933
@ c_style
Definition: numpy.h:660
@ f_style
Definition: numpy.h:661
\rst Holds a reference to a Python object (no reference counting)
Definition: pytypes.h:194
Definition: pytypes.h:1422
static handle cast(const itype &src, return_value_policy policy, handle parent)
Definition: pytypes.h:1167
typename std::enable_if< B, T >::type enable_if_t
from cpp_future import (convenient aliases from C++14/17)
Definition: common.h:625
PYBIND11_NOINLINE void pybind11_fail(const char *reason)
Thrown when pybind11::cast or.
Definition: common.h:992
#define PYBIND11_NAMESPACE_END(name)
Definition: common.h:21
#define PYBIND11_NAMESPACE_BEGIN(name)
Definition: common.h:20
typename void_t_impl< Ts... >::type void_t
Definition: common.h:694
return_value_policy
Approach used to cast a previously unknown C++ instance into a Python object.
Definition: common.h:470
constexpr descr< N - 1 > const_name(char const (&text)[N])
Definition: descr.h:60
constexpr descr< 0 > concat()
Definition: descr.h:139
PyArray_Proxy * array_proxy(void *ptr)
Definition: numpy.h:301
arr data(const arr &a, Ix... index)
py::array arr
#define PYBIND11_WARNING_DISABLE_GCC(name)
Definition: common.h:67
#define PYBIND11_WARNING_DISABLE_MSVC(name)
Definition: common.h:55
int flags
Definition: numpy.h:74
Eigen::Tensor< Scalar_, NumIndices_, Options_, IndexType > Type
Definition: tensor.h:55
static Eigen::DSizes< typename Type::Index, Type::NumIndices > get_shape(const Type &f)
Definition: tensor.h:58
static constexpr bool is_correct_shape(const Eigen::DSizes< typename Type::Index, Type::NumIndices > &)
Definition: tensor.h:63
static constexpr Eigen::DSizes< typename Type::Index, Type::NumIndices > get_shape()
Definition: tensor.h:97
static bool is_correct_shape(const Eigen::DSizes< typename Type::Index, Type::NumIndices > &shape)
Definition: tensor.h:102
Eigen::TensorFixedSize< Scalar_, Eigen::Sizes< Indices... >, Options_, IndexType > Type
Definition: tensor.h:89
static constexpr Eigen::DSizes< typename Type::Index, Type::NumIndices > get_shape(const Type &)
Definition: tensor.h:93
static constexpr auto details
Definition: tensor.h:124
static constexpr auto value
Definition: tensor.h:128
Index sequences.
Definition: common.h:652
Annotation for function names.
Definition: attr.h:47
@ NPY_ARRAY_WRITEABLE_
Definition: numpy.h:146
static handle cast(const MapType *src, return_value_policy policy, handle parent)
Definition: tensor.h:440
static handle cast(MapType &src, return_value_policy policy, handle parent)
Definition: tensor.h:415
static handle cast(const MapType &&src, return_value_policy policy, handle parent)
Definition: tensor.h:411
static handle cast(MapType *src, return_value_policy policy, handle parent)
Definition: tensor.h:431
static handle cast(MapType &&src, return_value_policy policy, handle parent)
Definition: tensor.h:407
static handle cast(const MapType &src, return_value_policy policy, handle parent)
Definition: tensor.h:423
static handle cast(Type &src, return_value_policy policy, handle parent)
Definition: tensor.h:230
static handle cast(Type *src, return_value_policy policy, handle parent)
Definition: tensor.h:246
static handle cast(Type &&src, return_value_policy policy, handle parent)
Definition: tensor.h:214
static handle cast(const Type &src, return_value_policy policy, handle parent)
Definition: tensor.h:238
static handle cast(const Type *src, return_value_policy policy, handle parent)
Definition: tensor.h:255
static handle cast_impl(C *src, return_value_policy policy, handle parent)
Definition: tensor.h:265
static handle cast(const Type &&src, return_value_policy policy, handle parent)
Definition: tensor.h:222
StoragePointerType get_array_data_for_type(array &arr)
Definition: tensor.h:330
bool is_tensor_aligned(const void *data)
Definition: tensor.h:37
Eigen::DSizes< T, size > get_shape_for_array(const array &arr)
Definition: tensor.h:153
PYBIND11_WARNING_PUSH std::vector< T > convert_dsizes_to_vector(const Eigen::DSizes< T, size > &arr)
Definition: tensor.h:142
constexpr int compute_array_flag_from_tensor()
Definition: tensor.h:42
PYBIND11_WARNING_PUSH PYBIND11_WARNING_POP
Definition: tensor.h:29