File indexing completed on 2025-01-18 10:17:45
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #pragma once
0011
0012 #include "../numpy.h"
0013
0014
0015
0016
0017
0018
0019 PYBIND11_WARNING_PUSH
0020 PYBIND11_WARNING_DISABLE_MSVC(5054)
0021
0022 PYBIND11_WARNING_DISABLE_GCC("-Wmaybe-uninitialized")
0023
0024 #include <Eigen/Core>
0025 #include <Eigen/SparseCore>
0026
0027 PYBIND11_WARNING_POP
0028
0029
0030
0031
0032 static_assert(EIGEN_VERSION_AT_LEAST(3, 2, 7),
0033 "Eigen matrix support in pybind11 requires Eigen >= 3.2.7");
0034
0035 PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
0036
0037 PYBIND11_WARNING_DISABLE_MSVC(4127)
0038
0039
0040 using EigenDStride = Eigen::Stride<Eigen::Dynamic, Eigen::Dynamic>;
0041 template <typename MatrixType>
0042 using EigenDRef = Eigen::Ref<MatrixType, 0, EigenDStride>;
0043 template <typename MatrixType>
0044 using EigenDMap = Eigen::Map<MatrixType, 0, EigenDStride>;
0045
0046 PYBIND11_NAMESPACE_BEGIN(detail)
0047
0048 #if EIGEN_VERSION_AT_LEAST(3, 3, 0)
0049 using EigenIndex = Eigen::Index;
0050 template <typename Scalar, int Flags, typename StorageIndex>
0051 using EigenMapSparseMatrix = Eigen::Map<Eigen::SparseMatrix<Scalar, Flags, StorageIndex>>;
0052 #else
0053 using EigenIndex = EIGEN_DEFAULT_DENSE_INDEX_TYPE;
0054 template <typename Scalar, int Flags, typename StorageIndex>
0055 using EigenMapSparseMatrix = Eigen::MappedSparseMatrix<Scalar, Flags, StorageIndex>;
0056 #endif
0057
0058
0059 template <typename T>
0060 using is_eigen_dense_map = all_of<is_template_base_of<Eigen::DenseBase, T>,
0061 std::is_base_of<Eigen::MapBase<T, Eigen::ReadOnlyAccessors>, T>>;
0062 template <typename T>
0063 using is_eigen_mutable_map = std::is_base_of<Eigen::MapBase<T, Eigen::WriteAccessors>, T>;
0064 template <typename T>
0065 using is_eigen_dense_plain
0066 = all_of<negation<is_eigen_dense_map<T>>, is_template_base_of<Eigen::PlainObjectBase, T>>;
0067 template <typename T>
0068 using is_eigen_sparse = is_template_base_of<Eigen::SparseMatrixBase, T>;
0069
0070
0071
0072
0073 template <typename T>
0074 using is_eigen_other
0075 = all_of<is_template_base_of<Eigen::EigenBase, T>,
0076 negation<any_of<is_eigen_dense_map<T>, is_eigen_dense_plain<T>, is_eigen_sparse<T>>>>;
0077
0078
0079 template <bool EigenRowMajor>
0080 struct EigenConformable {
0081 bool conformable = false;
0082 EigenIndex rows = 0, cols = 0;
0083 EigenDStride stride{0, 0};
0084 bool negativestrides = false;
0085
0086
0087 EigenConformable(bool fits = false) : conformable{fits} {}
0088
0089 EigenConformable(EigenIndex r, EigenIndex c, EigenIndex rstride, EigenIndex cstride)
0090 : conformable{true}, rows{r}, cols{c},
0091
0092
0093 stride{EigenRowMajor ? (rstride > 0 ? rstride : 0)
0094 : (cstride > 0 ? cstride : 0) ,
0095 EigenRowMajor ? (cstride > 0 ? cstride : 0)
0096 : (rstride > 0 ? rstride : 0) },
0097 negativestrides{rstride < 0 || cstride < 0} {}
0098
0099 EigenConformable(EigenIndex r, EigenIndex c, EigenIndex stride)
0100 : EigenConformable(r, c, r == 1 ? c * stride : stride, c == 1 ? r : r * stride) {}
0101
0102 template <typename props>
0103 bool stride_compatible() const {
0104
0105
0106
0107
0108 if (negativestrides) {
0109 return false;
0110 }
0111 if (rows == 0 || cols == 0) {
0112 return true;
0113 }
0114 return (props::inner_stride == Eigen::Dynamic || props::inner_stride == stride.inner()
0115 || (EigenRowMajor ? cols : rows) == 1)
0116 && (props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer()
0117 || (EigenRowMajor ? rows : cols) == 1);
0118 }
0119
0120 operator bool() const { return conformable; }
0121 };
0122
0123 template <typename Type>
0124 struct eigen_extract_stride {
0125 using type = Type;
0126 };
0127 template <typename PlainObjectType, int MapOptions, typename StrideType>
0128 struct eigen_extract_stride<Eigen::Map<PlainObjectType, MapOptions, StrideType>> {
0129 using type = StrideType;
0130 };
0131 template <typename PlainObjectType, int Options, typename StrideType>
0132 struct eigen_extract_stride<Eigen::Ref<PlainObjectType, Options, StrideType>> {
0133 using type = StrideType;
0134 };
0135
0136
0137 template <typename Type_>
0138 struct EigenProps {
0139 using Type = Type_;
0140 using Scalar = typename Type::Scalar;
0141 using StrideType = typename eigen_extract_stride<Type>::type;
0142 static constexpr EigenIndex rows = Type::RowsAtCompileTime, cols = Type::ColsAtCompileTime,
0143 size = Type::SizeAtCompileTime;
0144 static constexpr bool row_major = Type::IsRowMajor,
0145 vector
0146 = Type::IsVectorAtCompileTime,
0147 fixed_rows = rows != Eigen::Dynamic, fixed_cols = cols != Eigen::Dynamic,
0148 fixed = size != Eigen::Dynamic,
0149 dynamic = !fixed_rows && !fixed_cols;
0150
0151 template <EigenIndex i, EigenIndex ifzero>
0152 using if_zero = std::integral_constant<EigenIndex, i == 0 ? ifzero : i>;
0153 static constexpr EigenIndex inner_stride
0154 = if_zero<StrideType::InnerStrideAtCompileTime, 1>::value,
0155 outer_stride = if_zero < StrideType::OuterStrideAtCompileTime,
0156 vector ? size
0157 : row_major ? cols
0158 : rows > ::value;
0159 static constexpr bool dynamic_stride
0160 = inner_stride == Eigen::Dynamic && outer_stride == Eigen::Dynamic;
0161 static constexpr bool requires_row_major
0162 = !dynamic_stride && !vector && (row_major ? inner_stride : outer_stride) == 1;
0163 static constexpr bool requires_col_major
0164 = !dynamic_stride && !vector && (row_major ? outer_stride : inner_stride) == 1;
0165
0166
0167
0168
0169 static EigenConformable<row_major> conformable(const array &a) {
0170 const auto dims = a.ndim();
0171 if (dims < 1 || dims > 2) {
0172 return false;
0173 }
0174
0175 if (dims == 2) {
0176
0177 EigenIndex np_rows = a.shape(0), np_cols = a.shape(1),
0178 np_rstride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar)),
0179 np_cstride = a.strides(1) / static_cast<ssize_t>(sizeof(Scalar));
0180 if ((fixed_rows && np_rows != rows) || (fixed_cols && np_cols != cols)) {
0181 return false;
0182 }
0183
0184 return {np_rows, np_cols, np_rstride, np_cstride};
0185 }
0186
0187
0188
0189 const EigenIndex n = a.shape(0),
0190 stride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar));
0191
0192 if (vector) {
0193 if (fixed && size != n) {
0194 return false;
0195 }
0196 return {rows == 1 ? 1 : n, cols == 1 ? 1 : n, stride};
0197 }
0198 if (fixed) {
0199
0200 return false;
0201 }
0202 if (fixed_cols) {
0203
0204
0205 if (cols != n) {
0206 return false;
0207 }
0208 return {1, n, stride};
0209 }
0210 if (fixed_rows && rows != n) {
0211 return false;
0212 }
0213 return {n, 1, stride};
0214 }
0215
0216 static constexpr bool show_writeable
0217 = is_eigen_dense_map<Type>::value && is_eigen_mutable_map<Type>::value;
0218 static constexpr bool show_order = is_eigen_dense_map<Type>::value;
0219 static constexpr bool show_c_contiguous = show_order && requires_row_major;
0220 static constexpr bool show_f_contiguous
0221 = !show_c_contiguous && show_order && requires_col_major;
0222
0223 static constexpr auto descriptor
0224 = const_name("numpy.ndarray[") + npy_format_descriptor<Scalar>::name + const_name("[")
0225 + const_name<fixed_rows>(const_name<(size_t) rows>(), const_name("m")) + const_name(", ")
0226 + const_name<fixed_cols>(const_name<(size_t) cols>(), const_name("n")) + const_name("]")
0227 +
0228
0229
0230
0231
0232
0233
0234 const_name<show_writeable>(", flags.writeable", "")
0235 + const_name<show_c_contiguous>(", flags.c_contiguous", "")
0236 + const_name<show_f_contiguous>(", flags.f_contiguous", "") + const_name("]");
0237 };
0238
0239
0240
0241 template <typename props>
0242 handle
0243 eigen_array_cast(typename props::Type const &src, handle base = handle(), bool writeable = true) {
0244 constexpr ssize_t elem_size = sizeof(typename props::Scalar);
0245 array a;
0246 if (props::vector) {
0247 a = array({src.size()}, {elem_size * src.innerStride()}, src.data(), base);
0248 } else {
0249 a = array({src.rows(), src.cols()},
0250 {elem_size * src.rowStride(), elem_size * src.colStride()},
0251 src.data(),
0252 base);
0253 }
0254
0255 if (!writeable) {
0256 array_proxy(a.ptr())->flags &= ~detail::npy_api::NPY_ARRAY_WRITEABLE_;
0257 }
0258
0259 return a.release();
0260 }
0261
0262
0263
0264
0265
0266 template <typename props, typename Type>
0267 handle eigen_ref_array(Type &src, handle parent = none()) {
0268
0269
0270 return eigen_array_cast<props>(src, parent, !std::is_const<Type>::value);
0271 }
0272
0273
0274
0275
0276
0277 template <typename props, typename Type, typename = enable_if_t<is_eigen_dense_plain<Type>::value>>
0278 handle eigen_encapsulate(Type *src) {
0279 capsule base(src, [](void *o) { delete static_cast<Type *>(o); });
0280 return eigen_ref_array<props>(*src, base);
0281 }
0282
0283
0284
0285 template <typename Type>
0286 struct type_caster<Type, enable_if_t<is_eigen_dense_plain<Type>::value>> {
0287 using Scalar = typename Type::Scalar;
0288 using props = EigenProps<Type>;
0289
0290 bool load(handle src, bool convert) {
0291
0292 if (!convert && !isinstance<array_t<Scalar>>(src)) {
0293 return false;
0294 }
0295
0296
0297 auto buf = array::ensure(src);
0298
0299 if (!buf) {
0300 return false;
0301 }
0302
0303 auto dims = buf.ndim();
0304 if (dims < 1 || dims > 2) {
0305 return false;
0306 }
0307
0308 auto fits = props::conformable(buf);
0309 if (!fits) {
0310 return false;
0311 }
0312
0313
0314 value = Type(fits.rows, fits.cols);
0315 auto ref = reinterpret_steal<array>(eigen_ref_array<props>(value));
0316 if (dims == 1) {
0317 ref = ref.squeeze();
0318 } else if (ref.ndim() == 1) {
0319 buf = buf.squeeze();
0320 }
0321
0322 int result = detail::npy_api::get().PyArray_CopyInto_(ref.ptr(), buf.ptr());
0323
0324 if (result < 0) {
0325 PyErr_Clear();
0326 return false;
0327 }
0328
0329 return true;
0330 }
0331
0332 private:
0333
0334 template <typename CType>
0335 static handle cast_impl(CType *src, return_value_policy policy, handle parent) {
0336 switch (policy) {
0337 case return_value_policy::take_ownership:
0338 case return_value_policy::automatic:
0339 return eigen_encapsulate<props>(src);
0340 case return_value_policy::move:
0341 return eigen_encapsulate<props>(new CType(std::move(*src)));
0342 case return_value_policy::copy:
0343 return eigen_array_cast<props>(*src);
0344 case return_value_policy::reference:
0345 case return_value_policy::automatic_reference:
0346 return eigen_ref_array<props>(*src);
0347 case return_value_policy::reference_internal:
0348 return eigen_ref_array<props>(*src, parent);
0349 default:
0350 throw cast_error("unhandled return_value_policy: should not happen!");
0351 };
0352 }
0353
0354 public:
0355
0356 static handle cast(Type &&src, return_value_policy , handle parent) {
0357 return cast_impl(&src, return_value_policy::move, parent);
0358 }
0359
0360 static handle cast(const Type &&src, return_value_policy , handle parent) {
0361 return cast_impl(&src, return_value_policy::move, parent);
0362 }
0363
0364 static handle cast(Type &src, return_value_policy policy, handle parent) {
0365 if (policy == return_value_policy::automatic
0366 || policy == return_value_policy::automatic_reference) {
0367 policy = return_value_policy::copy;
0368 }
0369 return cast_impl(&src, policy, parent);
0370 }
0371
0372 static handle cast(const Type &src, return_value_policy policy, handle parent) {
0373 if (policy == return_value_policy::automatic
0374 || policy == return_value_policy::automatic_reference) {
0375 policy = return_value_policy::copy;
0376 }
0377 return cast(&src, policy, parent);
0378 }
0379
0380 static handle cast(Type *src, return_value_policy policy, handle parent) {
0381 return cast_impl(src, policy, parent);
0382 }
0383
0384 static handle cast(const Type *src, return_value_policy policy, handle parent) {
0385 return cast_impl(src, policy, parent);
0386 }
0387
0388 static constexpr auto name = props::descriptor;
0389
0390
0391 operator Type *() { return &value; }
0392
0393 operator Type &() { return value; }
0394
0395 operator Type &&() && { return std::move(value); }
0396 template <typename T>
0397 using cast_op_type = movable_cast_op_type<T>;
0398
0399 private:
0400 Type value;
0401 };
0402
0403
0404 template <typename MapType>
0405 struct eigen_map_caster {
0406 private:
0407 using props = EigenProps<MapType>;
0408
0409 public:
0410
0411
0412
0413
0414
0415
0416 static handle cast(const MapType &src, return_value_policy policy, handle parent) {
0417 switch (policy) {
0418 case return_value_policy::copy:
0419 return eigen_array_cast<props>(src);
0420 case return_value_policy::reference_internal:
0421 return eigen_array_cast<props>(src, parent, is_eigen_mutable_map<MapType>::value);
0422 case return_value_policy::reference:
0423 case return_value_policy::automatic:
0424 case return_value_policy::automatic_reference:
0425 return eigen_array_cast<props>(src, none(), is_eigen_mutable_map<MapType>::value);
0426 default:
0427
0428 pybind11_fail("Invalid return_value_policy for Eigen Map/Ref/Block type");
0429 }
0430 }
0431
0432 static constexpr auto name = props::descriptor;
0433
0434
0435
0436
0437 bool load(handle, bool) = delete;
0438 operator MapType() = delete;
0439 template <typename>
0440 using cast_op_type = MapType;
0441 };
0442
0443
0444 template <typename Type>
0445 struct type_caster<Type, enable_if_t<is_eigen_dense_map<Type>::value>> : eigen_map_caster<Type> {};
0446
0447
0448
0449 template <typename PlainObjectType, typename StrideType>
0450 struct type_caster<
0451 Eigen::Ref<PlainObjectType, 0, StrideType>,
0452 enable_if_t<is_eigen_dense_map<Eigen::Ref<PlainObjectType, 0, StrideType>>::value>>
0453 : public eigen_map_caster<Eigen::Ref<PlainObjectType, 0, StrideType>> {
0454 private:
0455 using Type = Eigen::Ref<PlainObjectType, 0, StrideType>;
0456 using props = EigenProps<Type>;
0457 using Scalar = typename props::Scalar;
0458 using MapType = Eigen::Map<PlainObjectType, 0, StrideType>;
0459 using Array
0460 = array_t<Scalar,
0461 array::forcecast
0462 | ((props::row_major ? props::inner_stride : props::outer_stride) == 1
0463 ? array::c_style
0464 : (props::row_major ? props::outer_stride : props::inner_stride) == 1
0465 ? array::f_style
0466 : 0)>;
0467 static constexpr bool need_writeable = is_eigen_mutable_map<Type>::value;
0468
0469 std::unique_ptr<MapType> map;
0470 std::unique_ptr<Type> ref;
0471
0472
0473
0474
0475
0476
0477 Array copy_or_ref;
0478
0479 public:
0480 bool load(handle src, bool convert) {
0481
0482
0483 bool need_copy = !isinstance<Array>(src);
0484
0485 EigenConformable<props::row_major> fits;
0486 if (!need_copy) {
0487
0488
0489 auto aref = reinterpret_borrow<Array>(src);
0490
0491 if (aref && (!need_writeable || aref.writeable())) {
0492 fits = props::conformable(aref);
0493 if (!fits) {
0494 return false;
0495 }
0496 if (!fits.template stride_compatible<props>()) {
0497 need_copy = true;
0498 } else {
0499 copy_or_ref = std::move(aref);
0500 }
0501 } else {
0502 need_copy = true;
0503 }
0504 }
0505
0506 if (need_copy) {
0507
0508
0509
0510 if (!convert || need_writeable) {
0511 return false;
0512 }
0513
0514 Array copy = Array::ensure(src);
0515 if (!copy) {
0516 return false;
0517 }
0518 fits = props::conformable(copy);
0519 if (!fits || !fits.template stride_compatible<props>()) {
0520 return false;
0521 }
0522 copy_or_ref = std::move(copy);
0523 loader_life_support::add_patient(copy_or_ref);
0524 }
0525
0526 ref.reset();
0527 map.reset(new MapType(data(copy_or_ref),
0528 fits.rows,
0529 fits.cols,
0530 make_stride(fits.stride.outer(), fits.stride.inner())));
0531 ref.reset(new Type(*map));
0532
0533 return true;
0534 }
0535
0536
0537 operator Type *() { return ref.get(); }
0538
0539 operator Type &() { return *ref; }
0540 template <typename _T>
0541 using cast_op_type = pybind11::detail::cast_op_type<_T>;
0542
0543 private:
0544 template <typename T = Type, enable_if_t<is_eigen_mutable_map<T>::value, int> = 0>
0545 Scalar *data(Array &a) {
0546 return a.mutable_data();
0547 }
0548
0549 template <typename T = Type, enable_if_t<!is_eigen_mutable_map<T>::value, int> = 0>
0550 const Scalar *data(Array &a) {
0551 return a.data();
0552 }
0553
0554
0555
0556 template <typename S>
0557 using stride_ctor_default = bool_constant<S::InnerStrideAtCompileTime != Eigen::Dynamic
0558 && S::OuterStrideAtCompileTime != Eigen::Dynamic
0559 && std::is_default_constructible<S>::value>;
0560
0561
0562 template <typename S>
0563 using stride_ctor_dual
0564 = bool_constant<!stride_ctor_default<S>::value
0565 && std::is_constructible<S, EigenIndex, EigenIndex>::value>;
0566
0567
0568 template <typename S>
0569 using stride_ctor_outer
0570 = bool_constant<!any_of<stride_ctor_default<S>, stride_ctor_dual<S>>::value
0571 && S::OuterStrideAtCompileTime == Eigen::Dynamic
0572 && S::InnerStrideAtCompileTime != Eigen::Dynamic
0573 && std::is_constructible<S, EigenIndex>::value>;
0574 template <typename S>
0575 using stride_ctor_inner
0576 = bool_constant<!any_of<stride_ctor_default<S>, stride_ctor_dual<S>>::value
0577 && S::InnerStrideAtCompileTime == Eigen::Dynamic
0578 && S::OuterStrideAtCompileTime != Eigen::Dynamic
0579 && std::is_constructible<S, EigenIndex>::value>;
0580
0581 template <typename S = StrideType, enable_if_t<stride_ctor_default<S>::value, int> = 0>
0582 static S make_stride(EigenIndex, EigenIndex) {
0583 return S();
0584 }
0585 template <typename S = StrideType, enable_if_t<stride_ctor_dual<S>::value, int> = 0>
0586 static S make_stride(EigenIndex outer, EigenIndex inner) {
0587 return S(outer, inner);
0588 }
0589 template <typename S = StrideType, enable_if_t<stride_ctor_outer<S>::value, int> = 0>
0590 static S make_stride(EigenIndex outer, EigenIndex) {
0591 return S(outer);
0592 }
0593 template <typename S = StrideType, enable_if_t<stride_ctor_inner<S>::value, int> = 0>
0594 static S make_stride(EigenIndex, EigenIndex inner) {
0595 return S(inner);
0596 }
0597 };
0598
0599
0600
0601
0602
0603 template <typename Type>
0604 struct type_caster<Type, enable_if_t<is_eigen_other<Type>::value>> {
0605 protected:
0606 using Matrix
0607 = Eigen::Matrix<typename Type::Scalar, Type::RowsAtCompileTime, Type::ColsAtCompileTime>;
0608 using props = EigenProps<Matrix>;
0609
0610 public:
0611 static handle cast(const Type &src, return_value_policy , handle ) {
0612 handle h = eigen_encapsulate<props>(new Matrix(src));
0613 return h;
0614 }
0615 static handle cast(const Type *src, return_value_policy policy, handle parent) {
0616 return cast(*src, policy, parent);
0617 }
0618
0619 static constexpr auto name = props::descriptor;
0620
0621
0622
0623
0624 bool load(handle, bool) = delete;
0625 operator Type() = delete;
0626 template <typename>
0627 using cast_op_type = Type;
0628 };
0629
0630 template <typename Type>
0631 struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
0632 using Scalar = typename Type::Scalar;
0633 using StorageIndex = remove_reference_t<decltype(*std::declval<Type>().outerIndexPtr())>;
0634 using Index = typename Type::Index;
0635 static constexpr bool rowMajor = Type::IsRowMajor;
0636
0637 bool load(handle src, bool) {
0638 if (!src) {
0639 return false;
0640 }
0641
0642 auto obj = reinterpret_borrow<object>(src);
0643 object sparse_module = module_::import("scipy.sparse");
0644 object matrix_type = sparse_module.attr(rowMajor ? "csr_matrix" : "csc_matrix");
0645
0646 if (!type::handle_of(obj).is(matrix_type)) {
0647 try {
0648 obj = matrix_type(obj);
0649 } catch (const error_already_set &) {
0650 return false;
0651 }
0652 }
0653
0654 auto values = array_t<Scalar>((object) obj.attr("data"));
0655 auto innerIndices = array_t<StorageIndex>((object) obj.attr("indices"));
0656 auto outerIndices = array_t<StorageIndex>((object) obj.attr("indptr"));
0657 auto shape = pybind11::tuple((pybind11::object) obj.attr("shape"));
0658 auto nnz = obj.attr("nnz").cast<Index>();
0659
0660 if (!values || !innerIndices || !outerIndices) {
0661 return false;
0662 }
0663
0664 value = EigenMapSparseMatrix<Scalar,
0665 Type::Flags &(Eigen::RowMajor | Eigen::ColMajor),
0666 StorageIndex>(shape[0].cast<Index>(),
0667 shape[1].cast<Index>(),
0668 std::move(nnz),
0669 outerIndices.mutable_data(),
0670 innerIndices.mutable_data(),
0671 values.mutable_data());
0672
0673 return true;
0674 }
0675
0676 static handle cast(const Type &src, return_value_policy , handle ) {
0677 const_cast<Type &>(src).makeCompressed();
0678
0679 object matrix_type
0680 = module_::import("scipy.sparse").attr(rowMajor ? "csr_matrix" : "csc_matrix");
0681
0682 array data(src.nonZeros(), src.valuePtr());
0683 array outerIndices((rowMajor ? src.rows() : src.cols()) + 1, src.outerIndexPtr());
0684 array innerIndices(src.nonZeros(), src.innerIndexPtr());
0685
0686 return matrix_type(pybind11::make_tuple(
0687 std::move(data), std::move(innerIndices), std::move(outerIndices)),
0688 pybind11::make_tuple(src.rows(), src.cols()))
0689 .release();
0690 }
0691
0692 PYBIND11_TYPE_CASTER(Type,
0693 const_name<(Type::IsRowMajor) != 0>("scipy.sparse.csr_matrix[",
0694 "scipy.sparse.csc_matrix[")
0695 + npy_format_descriptor<Scalar>::name + const_name("]"));
0696 };
0697
0698 PYBIND11_NAMESPACE_END(detail)
0699 PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)