Back to home page

EIC code displayed by LXR

 
 

    


Warning, file /jana2/src/python/externals/pybind11-2.10.3/include/pybind11/eigen/tensor.h was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002     pybind11/eigen/tensor.h: Transparent conversion for Eigen tensors
0003 
0004     All rights reserved. Use of this source code is governed by a
0005     BSD-style license that can be found in the LICENSE file.
0006 */
0007 
0008 #pragma once
0009 
0010 #include "../numpy.h"
0011 
0012 #if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
0013 static_assert(__GNUC__ > 5, "Eigen Tensor support in pybind11 requires GCC > 5.0");
0014 #endif
0015 
0016 // Disable warnings for Eigen
0017 PYBIND11_WARNING_PUSH
0018 PYBIND11_WARNING_DISABLE_MSVC(4554)
0019 PYBIND11_WARNING_DISABLE_MSVC(4127)
0020 PYBIND11_WARNING_DISABLE_GCC("-Wmaybe-uninitialized")
0021 
0022 #include <unsupported/Eigen/CXX11/Tensor>
0023 
0024 PYBIND11_WARNING_POP
0025 
0026 static_assert(EIGEN_VERSION_AT_LEAST(3, 3, 0),
0027               "Eigen Tensor support in pybind11 requires Eigen >= 3.3.0");
0028 
0029 PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
0030 
0031 PYBIND11_WARNING_DISABLE_MSVC(4127)
0032 
0033 PYBIND11_NAMESPACE_BEGIN(detail)
0034 
0035 inline bool is_tensor_aligned(const void *data) {
0036     return (reinterpret_cast<std::size_t>(data) % EIGEN_DEFAULT_ALIGN_BYTES) == 0;
0037 }
0038 
0039 template <typename T>
0040 constexpr int compute_array_flag_from_tensor() {
0041     static_assert((static_cast<int>(T::Layout) == static_cast<int>(Eigen::RowMajor))
0042                       || (static_cast<int>(T::Layout) == static_cast<int>(Eigen::ColMajor)),
0043                   "Layout must be row or column major");
0044     return (static_cast<int>(T::Layout) == static_cast<int>(Eigen::RowMajor)) ? array::c_style
0045                                                                               : array::f_style;
0046 }
0047 
0048 template <typename T>
0049 struct eigen_tensor_helper {};
0050 
0051 template <typename Scalar_, int NumIndices_, int Options_, typename IndexType>
0052 struct eigen_tensor_helper<Eigen::Tensor<Scalar_, NumIndices_, Options_, IndexType>> {
0053     using Type = Eigen::Tensor<Scalar_, NumIndices_, Options_, IndexType>;
0054     using ValidType = void;
0055 
0056     static Eigen::DSizes<typename Type::Index, Type::NumIndices> get_shape(const Type &f) {
0057         return f.dimensions();
0058     }
0059 
0060     static constexpr bool
0061     is_correct_shape(const Eigen::DSizes<typename Type::Index, Type::NumIndices> & /*shape*/) {
0062         return true;
0063     }
0064 
0065     template <typename T>
0066     struct helper {};
0067 
0068     template <size_t... Is>
0069     struct helper<index_sequence<Is...>> {
0070         static constexpr auto value = concat(const_name(((void) Is, "?"))...);
0071     };
0072 
0073     static constexpr auto dimensions_descriptor
0074         = helper<decltype(make_index_sequence<Type::NumIndices>())>::value;
0075 
0076     template <typename... Args>
0077     static Type *alloc(Args &&...args) {
0078         return new Type(std::forward<Args>(args)...);
0079     }
0080 
0081     static void free(Type *tensor) { delete tensor; }
0082 };
0083 
0084 template <typename Scalar_, typename std::ptrdiff_t... Indices, int Options_, typename IndexType>
0085 struct eigen_tensor_helper<
0086     Eigen::TensorFixedSize<Scalar_, Eigen::Sizes<Indices...>, Options_, IndexType>> {
0087     using Type = Eigen::TensorFixedSize<Scalar_, Eigen::Sizes<Indices...>, Options_, IndexType>;
0088     using ValidType = void;
0089 
0090     static constexpr Eigen::DSizes<typename Type::Index, Type::NumIndices>
0091     get_shape(const Type & /*f*/) {
0092         return get_shape();
0093     }
0094 
0095     static constexpr Eigen::DSizes<typename Type::Index, Type::NumIndices> get_shape() {
0096         return Eigen::DSizes<typename Type::Index, Type::NumIndices>(Indices...);
0097     }
0098 
0099     static bool
0100     is_correct_shape(const Eigen::DSizes<typename Type::Index, Type::NumIndices> &shape) {
0101         return get_shape() == shape;
0102     }
0103 
0104     static constexpr auto dimensions_descriptor = concat(const_name<Indices>()...);
0105 
0106     template <typename... Args>
0107     static Type *alloc(Args &&...args) {
0108         Eigen::aligned_allocator<Type> allocator;
0109         return ::new (allocator.allocate(1)) Type(std::forward<Args>(args)...);
0110     }
0111 
0112     static void free(Type *tensor) {
0113         Eigen::aligned_allocator<Type> allocator;
0114         tensor->~Type();
0115         allocator.deallocate(tensor, 1);
0116     }
0117 };
0118 
0119 template <typename Type, bool ShowDetails, bool NeedsWriteable = false>
0120 struct get_tensor_descriptor {
0121     static constexpr auto details
0122         = const_name<NeedsWriteable>(", flags.writeable", "")
0123           + const_name<static_cast<int>(Type::Layout) == static_cast<int>(Eigen::RowMajor)>(
0124               ", flags.c_contiguous", ", flags.f_contiguous");
0125     static constexpr auto value
0126         = const_name("numpy.ndarray[") + npy_format_descriptor<typename Type::Scalar>::name
0127           + const_name("[") + eigen_tensor_helper<remove_cv_t<Type>>::dimensions_descriptor
0128           + const_name("]") + const_name<ShowDetails>(details, const_name("")) + const_name("]");
0129 };
0130 
0131 // When EIGEN_AVOID_STL_ARRAY is defined, Eigen::DSizes<T, 0> does not have the begin() member
0132 // function. Falling back to a simple loop works around this issue.
0133 //
0134 // We need to disable the type-limits warning for the inner loop when size = 0.
0135 
0136 PYBIND11_WARNING_PUSH
0137 PYBIND11_WARNING_DISABLE_GCC("-Wtype-limits")
0138 
0139 template <typename T, int size>
0140 std::vector<T> convert_dsizes_to_vector(const Eigen::DSizes<T, size> &arr) {
0141     std::vector<T> result(size);
0142 
0143     for (size_t i = 0; i < size; i++) {
0144         result[i] = arr[i];
0145     }
0146 
0147     return result;
0148 }
0149 
0150 template <typename T, int size>
0151 Eigen::DSizes<T, size> get_shape_for_array(const array &arr) {
0152     Eigen::DSizes<T, size> result;
0153     const T *shape = arr.shape();
0154     for (size_t i = 0; i < size; i++) {
0155         result[i] = shape[i];
0156     }
0157 
0158     return result;
0159 }
0160 
0161 PYBIND11_WARNING_POP
0162 
0163 template <typename Type>
0164 struct type_caster<Type, typename eigen_tensor_helper<Type>::ValidType> {
0165     using Helper = eigen_tensor_helper<Type>;
0166     static constexpr auto temp_name = get_tensor_descriptor<Type, false>::value;
0167     PYBIND11_TYPE_CASTER(Type, temp_name);
0168 
0169     bool load(handle src, bool convert) {
0170         if (!convert) {
0171             if (!isinstance<array>(src)) {
0172                 return false;
0173             }
0174             array temp = array::ensure(src);
0175             if (!temp) {
0176                 return false;
0177             }
0178 
0179             if (!temp.dtype().is(dtype::of<typename Type::Scalar>())) {
0180                 return false;
0181             }
0182         }
0183 
0184         array_t<typename Type::Scalar, compute_array_flag_from_tensor<Type>()> arr(
0185             reinterpret_borrow<object>(src));
0186 
0187         if (arr.ndim() != Type::NumIndices) {
0188             return false;
0189         }
0190         auto shape = get_shape_for_array<typename Type::Index, Type::NumIndices>(arr);
0191 
0192         if (!Helper::is_correct_shape(shape)) {
0193             return false;
0194         }
0195 
0196 #if EIGEN_VERSION_AT_LEAST(3, 4, 0)
0197         auto data_pointer = arr.data();
0198 #else
0199         // Handle Eigen bug
0200         auto data_pointer = const_cast<typename Type::Scalar *>(arr.data());
0201 #endif
0202 
0203         if (is_tensor_aligned(arr.data())) {
0204             value = Eigen::TensorMap<const Type, Eigen::Aligned>(data_pointer, shape);
0205         } else {
0206             value = Eigen::TensorMap<const Type>(data_pointer, shape);
0207         }
0208 
0209         return true;
0210     }
0211 
0212     static handle cast(Type &&src, return_value_policy policy, handle parent) {
0213         if (policy == return_value_policy::reference
0214             || policy == return_value_policy::reference_internal) {
0215             pybind11_fail("Cannot use a reference return value policy for an rvalue");
0216         }
0217         return cast_impl(&src, return_value_policy::move, parent);
0218     }
0219 
0220     static handle cast(const Type &&src, return_value_policy policy, handle parent) {
0221         if (policy == return_value_policy::reference
0222             || policy == return_value_policy::reference_internal) {
0223             pybind11_fail("Cannot use a reference return value policy for an rvalue");
0224         }
0225         return cast_impl(&src, return_value_policy::move, parent);
0226     }
0227 
0228     static handle cast(Type &src, return_value_policy policy, handle parent) {
0229         if (policy == return_value_policy::automatic
0230             || policy == return_value_policy::automatic_reference) {
0231             policy = return_value_policy::copy;
0232         }
0233         return cast_impl(&src, policy, parent);
0234     }
0235 
0236     static handle cast(const Type &src, return_value_policy policy, handle parent) {
0237         if (policy == return_value_policy::automatic
0238             || policy == return_value_policy::automatic_reference) {
0239             policy = return_value_policy::copy;
0240         }
0241         return cast(&src, policy, parent);
0242     }
0243 
0244     static handle cast(Type *src, return_value_policy policy, handle parent) {
0245         if (policy == return_value_policy::automatic) {
0246             policy = return_value_policy::take_ownership;
0247         } else if (policy == return_value_policy::automatic_reference) {
0248             policy = return_value_policy::reference;
0249         }
0250         return cast_impl(src, policy, parent);
0251     }
0252 
0253     static handle cast(const Type *src, return_value_policy policy, handle parent) {
0254         if (policy == return_value_policy::automatic) {
0255             policy = return_value_policy::take_ownership;
0256         } else if (policy == return_value_policy::automatic_reference) {
0257             policy = return_value_policy::reference;
0258         }
0259         return cast_impl(src, policy, parent);
0260     }
0261 
0262     template <typename C>
0263     static handle cast_impl(C *src, return_value_policy policy, handle parent) {
0264         object parent_object;
0265         bool writeable = false;
0266         switch (policy) {
0267             case return_value_policy::move:
0268                 if (std::is_const<C>::value) {
0269                     pybind11_fail("Cannot move from a constant reference");
0270                 }
0271 
0272                 src = Helper::alloc(std::move(*src));
0273 
0274                 parent_object
0275                     = capsule(src, [](void *ptr) { Helper::free(reinterpret_cast<Type *>(ptr)); });
0276                 writeable = true;
0277                 break;
0278 
0279             case return_value_policy::take_ownership:
0280                 if (std::is_const<C>::value) {
0281                     // This cast is ugly, and might be UB in some cases, but we don't have an
0282                     // alternative here as we must free that memory
0283                     Helper::free(const_cast<Type *>(src));
0284                     pybind11_fail("Cannot take ownership of a const reference");
0285                 }
0286 
0287                 parent_object
0288                     = capsule(src, [](void *ptr) { Helper::free(reinterpret_cast<Type *>(ptr)); });
0289                 writeable = true;
0290                 break;
0291 
0292             case return_value_policy::copy:
0293                 writeable = true;
0294                 break;
0295 
0296             case return_value_policy::reference:
0297                 parent_object = none();
0298                 writeable = !std::is_const<C>::value;
0299                 break;
0300 
0301             case return_value_policy::reference_internal:
0302                 // Default should do the right thing
0303                 if (!parent) {
0304                     pybind11_fail("Cannot use reference internal when there is no parent");
0305                 }
0306                 parent_object = reinterpret_borrow<object>(parent);
0307                 writeable = !std::is_const<C>::value;
0308                 break;
0309 
0310             default:
0311                 pybind11_fail("pybind11 bug in eigen.h, please file a bug report");
0312         }
0313 
0314         auto result = array_t<typename Type::Scalar, compute_array_flag_from_tensor<Type>()>(
0315             convert_dsizes_to_vector(Helper::get_shape(*src)), src->data(), parent_object);
0316 
0317         if (!writeable) {
0318             array_proxy(result.ptr())->flags &= ~detail::npy_api::NPY_ARRAY_WRITEABLE_;
0319         }
0320 
0321         return result.release();
0322     }
0323 };
0324 
0325 template <typename StoragePointerType,
0326           bool needs_writeable,
0327           enable_if_t<!needs_writeable, bool> = true>
0328 StoragePointerType get_array_data_for_type(array &arr) {
0329 #if EIGEN_VERSION_AT_LEAST(3, 4, 0)
0330     return reinterpret_cast<StoragePointerType>(arr.data());
0331 #else
0332     // Handle Eigen bug
0333     return reinterpret_cast<StoragePointerType>(const_cast<void *>(arr.data()));
0334 #endif
0335 }
0336 
0337 template <typename StoragePointerType,
0338           bool needs_writeable,
0339           enable_if_t<needs_writeable, bool> = true>
0340 StoragePointerType get_array_data_for_type(array &arr) {
0341     return reinterpret_cast<StoragePointerType>(arr.mutable_data());
0342 }
0343 
0344 template <typename T, typename = void>
0345 struct get_storage_pointer_type;
0346 
0347 template <typename MapType>
0348 struct get_storage_pointer_type<MapType, void_t<typename MapType::StoragePointerType>> {
0349     using SPT = typename MapType::StoragePointerType;
0350 };
0351 
0352 template <typename MapType>
0353 struct get_storage_pointer_type<MapType, void_t<typename MapType::PointerArgType>> {
0354     using SPT = typename MapType::PointerArgType;
0355 };
0356 
0357 template <typename Type, int Options>
0358 struct type_caster<Eigen::TensorMap<Type, Options>,
0359                    typename eigen_tensor_helper<remove_cv_t<Type>>::ValidType> {
0360     using MapType = Eigen::TensorMap<Type, Options>;
0361     using Helper = eigen_tensor_helper<remove_cv_t<Type>>;
0362 
0363     bool load(handle src, bool /*convert*/) {
0364         // Note that we have a lot more checks here as we want to make sure to avoid copies
0365         if (!isinstance<array>(src)) {
0366             return false;
0367         }
0368         auto arr = reinterpret_borrow<array>(src);
0369         if ((arr.flags() & compute_array_flag_from_tensor<Type>()) == 0) {
0370             return false;
0371         }
0372 
0373         if (!arr.dtype().is(dtype::of<typename Type::Scalar>())) {
0374             return false;
0375         }
0376 
0377         if (arr.ndim() != Type::NumIndices) {
0378             return false;
0379         }
0380 
0381         constexpr bool is_aligned = (Options & Eigen::Aligned) != 0;
0382 
0383         if (is_aligned && !is_tensor_aligned(arr.data())) {
0384             return false;
0385         }
0386 
0387         auto shape = get_shape_for_array<typename Type::Index, Type::NumIndices>(arr);
0388 
0389         if (!Helper::is_correct_shape(shape)) {
0390             return false;
0391         }
0392 
0393         if (needs_writeable && !arr.writeable()) {
0394             return false;
0395         }
0396 
0397         auto result = get_array_data_for_type<typename get_storage_pointer_type<MapType>::SPT,
0398                                               needs_writeable>(arr);
0399 
0400         value.reset(new MapType(std::move(result), std::move(shape)));
0401 
0402         return true;
0403     }
0404 
0405     static handle cast(MapType &&src, return_value_policy policy, handle parent) {
0406         return cast_impl(&src, policy, parent);
0407     }
0408 
0409     static handle cast(const MapType &&src, return_value_policy policy, handle parent) {
0410         return cast_impl(&src, policy, parent);
0411     }
0412 
0413     static handle cast(MapType &src, return_value_policy policy, handle parent) {
0414         if (policy == return_value_policy::automatic
0415             || policy == return_value_policy::automatic_reference) {
0416             policy = return_value_policy::copy;
0417         }
0418         return cast_impl(&src, policy, parent);
0419     }
0420 
0421     static handle cast(const MapType &src, return_value_policy policy, handle parent) {
0422         if (policy == return_value_policy::automatic
0423             || policy == return_value_policy::automatic_reference) {
0424             policy = return_value_policy::copy;
0425         }
0426         return cast(&src, policy, parent);
0427     }
0428 
0429     static handle cast(MapType *src, return_value_policy policy, handle parent) {
0430         if (policy == return_value_policy::automatic) {
0431             policy = return_value_policy::take_ownership;
0432         } else if (policy == return_value_policy::automatic_reference) {
0433             policy = return_value_policy::reference;
0434         }
0435         return cast_impl(src, policy, parent);
0436     }
0437 
0438     static handle cast(const MapType *src, return_value_policy policy, handle parent) {
0439         if (policy == return_value_policy::automatic) {
0440             policy = return_value_policy::take_ownership;
0441         } else if (policy == return_value_policy::automatic_reference) {
0442             policy = return_value_policy::reference;
0443         }
0444         return cast_impl(src, policy, parent);
0445     }
0446 
0447     template <typename C>
0448     static handle cast_impl(C *src, return_value_policy policy, handle parent) {
0449         object parent_object;
0450         constexpr bool writeable = !std::is_const<C>::value;
0451         switch (policy) {
0452             case return_value_policy::reference:
0453                 parent_object = none();
0454                 break;
0455 
0456             case return_value_policy::reference_internal:
0457                 // Default should do the right thing
0458                 if (!parent) {
0459                     pybind11_fail("Cannot use reference internal when there is no parent");
0460                 }
0461                 parent_object = reinterpret_borrow<object>(parent);
0462                 break;
0463 
0464             case return_value_policy::take_ownership:
0465                 delete src;
0466                 // fallthrough
0467             default:
0468                 // move, take_ownership don't make any sense for a ref/map:
0469                 pybind11_fail("Invalid return_value_policy for Eigen Map type, must be either "
0470                               "reference or reference_internal");
0471         }
0472 
0473         auto result = array_t<typename Type::Scalar, compute_array_flag_from_tensor<Type>()>(
0474             convert_dsizes_to_vector(Helper::get_shape(*src)),
0475             src->data(),
0476             std::move(parent_object));
0477 
0478         if (!writeable) {
0479             array_proxy(result.ptr())->flags &= ~detail::npy_api::NPY_ARRAY_WRITEABLE_;
0480         }
0481 
0482         return result.release();
0483     }
0484 
0485 #if EIGEN_VERSION_AT_LEAST(3, 4, 0)
0486 
0487     static constexpr bool needs_writeable = !std::is_const<typename std::remove_pointer<
0488         typename get_storage_pointer_type<MapType>::SPT>::type>::value;
0489 #else
0490     // Handle Eigen bug
0491     static constexpr bool needs_writeable = !std::is_const<Type>::value;
0492 #endif
0493 
0494 protected:
0495     // TODO: Move to std::optional once std::optional has more support
0496     std::unique_ptr<MapType> value;
0497 
0498 public:
0499     static constexpr auto name = get_tensor_descriptor<Type, true, needs_writeable>::value;
0500     explicit operator MapType *() { return value.get(); }
0501     explicit operator MapType &() { return *value; }
0502     explicit operator MapType &&() && { return std::move(*value); }
0503 
0504     template <typename T_>
0505     using cast_op_type = ::pybind11::detail::movable_cast_op_type<T_>;
0506 };
0507 
0508 PYBIND11_NAMESPACE_END(detail)
0509 PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)