test_numpy_array.cpp 20 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
/*
    tests/test_numpy_array.cpp -- test core array functionality

    Copyright (c) 2016 Ivan Smirnov <i.s.smirnov@gmail.com>

    All rights reserved. Use of this source code is governed by a
    BSD-style license that can be found in the LICENSE file.
*/

#include "pybind11_tests.h"
11

12
13
14
#include <pybind11/numpy.h>
#include <pybind11/stl.h>

15
#include <cstdint>
16
#include <utility>
17

18
19
20
21
22
23
24
25
// Size / dtype checks.
struct DtypeCheck {
    py::dtype numpy{};
    py::dtype pybind11{};
};

template <typename T>
DtypeCheck get_dtype_check(const char* name) {
26
    py::module_ np = py::module_::import("numpy");
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
    DtypeCheck check{};
    check.numpy = np.attr("dtype")(np.attr(name));
    check.pybind11 = py::dtype::of<T>();
    return check;
}

std::vector<DtypeCheck> get_concrete_dtype_checks() {
    return {
        // Normalization
        get_dtype_check<std::int8_t>("int8"),
        get_dtype_check<std::uint8_t>("uint8"),
        get_dtype_check<std::int16_t>("int16"),
        get_dtype_check<std::uint16_t>("uint16"),
        get_dtype_check<std::int32_t>("int32"),
        get_dtype_check<std::uint32_t>("uint32"),
        get_dtype_check<std::int64_t>("int64"),
        get_dtype_check<std::uint64_t>("uint64")
    };
}

struct DtypeSizeCheck {
    std::string name{};
    int size_cpp{};
    int size_numpy{};
    // For debugging.
    py::dtype dtype{};
};

template <typename T>
DtypeSizeCheck get_dtype_size_check() {
    DtypeSizeCheck check{};
    check.name = py::type_id<T>();
    check.size_cpp = sizeof(T);
    check.dtype = py::dtype::of<T>();
    check.size_numpy = check.dtype.attr("itemsize").template cast<int>();
    return check;
}

std::vector<DtypeSizeCheck> get_platform_dtype_size_checks() {
    return {
        get_dtype_size_check<short>(),
        get_dtype_size_check<unsigned short>(),
        get_dtype_size_check<int>(),
        get_dtype_size_check<unsigned int>(),
        get_dtype_size_check<long>(),
        get_dtype_size_check<unsigned long>(),
        get_dtype_size_check<long long>(),
        get_dtype_size_check<unsigned long long>(),
    };
}

// Arrays.
79
80
using arr = py::array;
using arr_t = py::array_t<uint16_t, 0>;
81
static_assert(std::is_same<arr_t::value_type, uint16_t>::value, "");
82

83
template<typename... Ix> arr data(const arr& a, Ix... index) {
84
    return arr(a.nbytes() - a.offset_at(index...), (const uint8_t *) a.data(index...));
85
86
}

87
template<typename... Ix> arr data_t(const arr_t& a, Ix... index) {
88
    return arr(a.size() - a.index_at(index...), a.data(index...));
89
90
}

91
template<typename... Ix> arr& mutate_data(arr& a, Ix... index) {
92
    auto ptr = (uint8_t *) a.mutable_data(index...);
93
    for (py::ssize_t i = 0; i < a.nbytes() - a.offset_at(index...); i++)
94
95
96
97
        ptr[i] = (uint8_t) (ptr[i] * 2);
    return a;
}

98
template<typename... Ix> arr_t& mutate_data_t(arr_t& a, Ix... index) {
99
    auto ptr = a.mutable_data(index...);
100
    for (py::ssize_t i = 0; i < a.size() - a.index_at(index...); i++)
101
102
103
104
        ptr[i]++;
    return a;
}

105
106
107
108
109
template<typename... Ix> py::ssize_t index_at(const arr& a, Ix... idx) { return a.index_at(idx...); }
template<typename... Ix> py::ssize_t index_at_t(const arr_t& a, Ix... idx) { return a.index_at(idx...); }
template<typename... Ix> py::ssize_t offset_at(const arr& a, Ix... idx) { return a.offset_at(idx...); }
template<typename... Ix> py::ssize_t offset_at_t(const arr_t& a, Ix... idx) { return a.offset_at(idx...); }
template<typename... Ix> py::ssize_t at_t(const arr_t& a, Ix... idx) { return a.at(idx...); }
110
template<typename... Ix> arr_t& mutate_at_t(arr_t& a, Ix... idx) { a.mutable_at(idx...)++; return a; }
111
112
113
114
115
116
117

#define def_index_fn(name, type) \
    sm.def(#name, [](type a) { return name(a); }); \
    sm.def(#name, [](type a, int i) { return name(a, i); }); \
    sm.def(#name, [](type a, int i, int j) { return name(a, i, j); }); \
    sm.def(#name, [](type a, int i, int j, int k) { return name(a, i, j, k); });

118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
template <typename T, typename T2> py::handle auxiliaries(T &&r, T2 &&r2) {
    if (r.ndim() != 2) throw std::domain_error("error: ndim != 2");
    py::list l;
    l.append(*r.data(0, 0));
    l.append(*r2.mutable_data(0, 0));
    l.append(r.data(0, 1) == r2.mutable_data(0, 1));
    l.append(r.ndim());
    l.append(r.itemsize());
    l.append(r.shape(0));
    l.append(r.shape(1));
    l.append(r.size());
    l.append(r.nbytes());
    return l.release();
}

133
134
135
// note: declaration at local scope would create a dangling reference!
static int data_i = 42;

136
TEST_SUBMODULE(numpy_array, sm) {
137
    try { py::module_::import("numpy"); }
138
    catch (...) { return; }
139

140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
    // test_dtypes
    py::class_<DtypeCheck>(sm, "DtypeCheck")
        .def_readonly("numpy", &DtypeCheck::numpy)
        .def_readonly("pybind11", &DtypeCheck::pybind11)
        .def("__repr__", [](const DtypeCheck& self) {
            return py::str("<DtypeCheck numpy={} pybind11={}>").format(
                self.numpy, self.pybind11);
        });
    sm.def("get_concrete_dtype_checks", &get_concrete_dtype_checks);

    py::class_<DtypeSizeCheck>(sm, "DtypeSizeCheck")
        .def_readonly("name", &DtypeSizeCheck::name)
        .def_readonly("size_cpp", &DtypeSizeCheck::size_cpp)
        .def_readonly("size_numpy", &DtypeSizeCheck::size_numpy)
        .def("__repr__", [](const DtypeSizeCheck& self) {
            return py::str("<DtypeSizeCheck name='{}' size_cpp={} size_numpy={} dtype={}>").format(
                self.name, self.size_cpp, self.size_numpy, self.dtype);
        });
    sm.def("get_platform_dtype_size_checks", &get_platform_dtype_size_checks);

160
    // test_array_attributes
161
162
    sm.def("ndim", [](const arr& a) { return a.ndim(); });
    sm.def("shape", [](const arr& a) { return arr(a.ndim(), a.shape()); });
163
    sm.def("shape", [](const arr& a, py::ssize_t dim) { return a.shape(dim); });
164
    sm.def("strides", [](const arr& a) { return arr(a.ndim(), a.strides()); });
165
    sm.def("strides", [](const arr& a, py::ssize_t dim) { return a.strides(dim); });
166
167
168
169
170
171
    sm.def("writeable", [](const arr& a) { return a.writeable(); });
    sm.def("size", [](const arr& a) { return a.size(); });
    sm.def("itemsize", [](const arr& a) { return a.itemsize(); });
    sm.def("nbytes", [](const arr& a) { return a.nbytes(); });
    sm.def("owndata", [](const arr& a) { return a.owndata(); });

172
    // test_index_offset
173
174
175
176
    def_index_fn(index_at, const arr&);
    def_index_fn(index_at_t, const arr_t&);
    def_index_fn(offset_at, const arr&);
    def_index_fn(offset_at_t, const arr_t&);
177
178
179
180
    // test_data
    def_index_fn(data, const arr&);
    def_index_fn(data_t, const arr_t&);
    // test_mutate_data, test_mutate_readonly
181
182
183
184
    def_index_fn(mutate_data, arr&);
    def_index_fn(mutate_data_t, arr_t&);
    def_index_fn(at_t, const arr_t&);
    def_index_fn(mutate_at_t, arr_t&);
185

186
187
188
    // test_make_c_f_array
    sm.def("make_f_array", [] { return py::array_t<float>({ 2, 2 }, { 4, 8 }); });
    sm.def("make_c_array", [] { return py::array_t<float>({ 2, 2 }, { 8, 4 }); });
189

190
191
    // test_empty_shaped_array
    sm.def("make_empty_shaped_array", [] { return py::array(py::dtype("f"), {}, {}); });
192
193
    // test numpy scalars (empty shape, ndim==0)
    sm.def("scalar_int", []() { return py::array(py::dtype("i"), {}, {}, &data_i); });
194

195
    // test_wrap
196
    sm.def("wrap", [](const py::array &a) {
197
198
        return py::array(
            a.dtype(),
199
200
            {a.shape(), a.shape() + a.ndim()},
            {a.strides(), a.strides() + a.ndim()},
201
202
203
204
            a.data(),
            a
        );
    });
205

206
    // test_numpy_view
207
208
209
210
211
212
213
214
215
    struct ArrayClass {
        int data[2] = { 1, 2 };
        ArrayClass() { py::print("ArrayClass()"); }
        ~ArrayClass() { py::print("~ArrayClass()"); }
    };
    py::class_<ArrayClass>(sm, "ArrayClass")
        .def(py::init<>())
        .def("numpy_view", [](py::object &obj) {
            py::print("ArrayClass::numpy_view()");
216
            auto &a = obj.cast<ArrayClass&>();
217
218
219
            return py::array_t<int>({2}, {4}, a.data, obj);
        }
    );
220

221
    // test_cast_numpy_int64_to_uint64
Wenzel Jakob's avatar
Wenzel Jakob committed
222
    sm.def("function_taking_uint64", [](uint64_t) { });
223

224
    // test_isinstance
225
    sm.def("isinstance_untyped", [](py::object yes, py::object no) {
226
227
        return py::isinstance<py::array>(std::move(yes))
               && !py::isinstance<py::array>(std::move(no));
228
    });
229
    // NOLINTNEXTLINE(performance-unnecessary-value-param)
230
231
232
233
    sm.def("isinstance_typed", [](py::object o) {
        return py::isinstance<py::array_t<double>>(o) && !py::isinstance<py::array_t<int>>(o);
    });

234
    // test_constructors
235
236
237
238
239
240
241
    sm.def("default_constructors", []() {
        return py::dict(
            "array"_a=py::array(),
            "array_t<int32>"_a=py::array_t<std::int32_t>(),
            "array_t<double>"_a=py::array_t<double>()
        );
    });
242
    sm.def("converting_constructors", [](const py::object &o) {
243
244
245
246
247
248
        return py::dict(
            "array"_a=py::array(o),
            "array_t<int32>"_a=py::array_t<std::int32_t>(o),
            "array_t<double>"_a=py::array_t<double>(o)
        );
    });
249

250
    // test_overload_resolution
251
    // NOLINTNEXTLINE(performance-unnecessary-value-param)
252
    sm.def("overloaded", [](py::array_t<double>) { return "double"; });
253
    // NOLINTNEXTLINE(performance-unnecessary-value-param)
254
    sm.def("overloaded", [](py::array_t<float>) { return "float"; });
255
    // NOLINTNEXTLINE(performance-unnecessary-value-param)
256
    sm.def("overloaded", [](py::array_t<int>) { return "int"; });
257
    // NOLINTNEXTLINE(performance-unnecessary-value-param)
258
    sm.def("overloaded", [](py::array_t<unsigned short>) { return "unsigned short"; });
259
    // NOLINTNEXTLINE(performance-unnecessary-value-param)
260
    sm.def("overloaded", [](py::array_t<long long>) { return "long long"; });
261
    // NOLINTNEXTLINE(performance-unnecessary-value-param)
262
    sm.def("overloaded", [](py::array_t<std::complex<double>>) { return "double complex"; });
263
    // NOLINTNEXTLINE(performance-unnecessary-value-param)
264
265
    sm.def("overloaded", [](py::array_t<std::complex<float>>) { return "float complex"; });

266
    // NOLINTNEXTLINE(performance-unnecessary-value-param)
267
    sm.def("overloaded2", [](py::array_t<std::complex<double>>) { return "double complex"; });
268
    // NOLINTNEXTLINE(performance-unnecessary-value-param)
269
    sm.def("overloaded2", [](py::array_t<double>) { return "double"; });
270
    // NOLINTNEXTLINE(performance-unnecessary-value-param)
271
    sm.def("overloaded2", [](py::array_t<std::complex<float>>) { return "float complex"; });
272
    // NOLINTNEXTLINE(performance-unnecessary-value-param)
273
274
    sm.def("overloaded2", [](py::array_t<float>) { return "float"; });

275
276
    // [workaround(intel)] ICC 20/21 breaks with py::arg().stuff, using py::arg{}.stuff works.

277
    // Only accept the exact types:
278
    // NOLINTNEXTLINE(performance-unnecessary-value-param)
279
    sm.def("overloaded3", [](py::array_t<int>) { return "int"; }, py::arg{}.noconvert());
280
    // NOLINTNEXTLINE(performance-unnecessary-value-param)
281
    sm.def("overloaded3", [](py::array_t<double>) { return "double"; }, py::arg{}.noconvert());
282
283
284

    // Make sure we don't do unsafe coercion (e.g. float to int) when not using forcecast, but
    // rather that float gets converted via the safe (conversion to double) overload:
285
    // NOLINTNEXTLINE(performance-unnecessary-value-param)
286
    sm.def("overloaded4", [](py::array_t<long long, 0>) { return "long long"; });
287
    // NOLINTNEXTLINE(performance-unnecessary-value-param)
288
289
290
291
    sm.def("overloaded4", [](py::array_t<double, 0>) { return "double"; });

    // But we do allow conversion to int if forcecast is enabled (but only if no overload matches
    // without conversion)
292
    // NOLINTNEXTLINE(performance-unnecessary-value-param)
293
    sm.def("overloaded5", [](py::array_t<unsigned int>) { return "unsigned int"; });
294
    // NOLINTNEXTLINE(performance-unnecessary-value-param)
295
296
    sm.def("overloaded5", [](py::array_t<double>) { return "double"; });

297
    // test_greedy_string_overload
298
    // Issue 685: ndarray shouldn't go to std::string overload
299
    // NOLINTNEXTLINE(performance-unnecessary-value-param)
300
    sm.def("issue685", [](std::string) { return "string"; });
301
    // NOLINTNEXTLINE(performance-unnecessary-value-param)
302
    sm.def("issue685", [](py::array) { return "array"; });
303
    // NOLINTNEXTLINE(performance-unnecessary-value-param)
304
    sm.def("issue685", [](py::object) { return "other"; });
305

306
    // test_array_unchecked_fixed_dims
307
308
    sm.def("proxy_add2", [](py::array_t<double> a, double v) {
        auto r = a.mutable_unchecked<2>();
309
310
        for (py::ssize_t i = 0; i < r.shape(0); i++)
            for (py::ssize_t j = 0; j < r.shape(1); j++)
311
                r(i, j) += v;
312
    }, py::arg{}.noconvert(), py::arg());
313

314
315
316
    sm.def("proxy_init3", [](double start) {
        py::array_t<double, py::array::c_style> a({ 3, 3, 3 });
        auto r = a.mutable_unchecked<3>();
317
318
319
        for (py::ssize_t i = 0; i < r.shape(0); i++)
        for (py::ssize_t j = 0; j < r.shape(1); j++)
        for (py::ssize_t k = 0; k < r.shape(2); k++)
320
321
322
323
324
325
            r(i, j, k) = start++;
        return a;
    });
    sm.def("proxy_init3F", [](double start) {
        py::array_t<double, py::array::f_style> a({ 3, 3, 3 });
        auto r = a.mutable_unchecked<3>();
326
327
328
        for (py::ssize_t k = 0; k < r.shape(2); k++)
        for (py::ssize_t j = 0; j < r.shape(1); j++)
        for (py::ssize_t i = 0; i < r.shape(0); i++)
329
330
331
            r(i, j, k) = start++;
        return a;
    });
332
    sm.def("proxy_squared_L2_norm", [](const py::array_t<double> &a) {
333
334
        auto r = a.unchecked<1>();
        double sumsq = 0;
335
        for (py::ssize_t i = 0; i < r.shape(0); i++)
336
337
338
            sumsq += r[i] * r(i); // Either notation works for a 1D array
        return sumsq;
    });
339
340
341
342
343
344
345

    sm.def("proxy_auxiliaries2", [](py::array_t<double> a) {
        auto r = a.unchecked<2>();
        auto r2 = a.mutable_unchecked<2>();
        return auxiliaries(r, r2);
    });

346
347
348
349
350
351
352
353
354
355
356
357
    sm.def("proxy_auxiliaries1_const_ref", [](py::array_t<double> a) {
        const auto &r = a.unchecked<1>();
        const auto &r2 = a.mutable_unchecked<1>();
        return r(0) == r2(0) && r[0] == r2[0];
    });

    sm.def("proxy_auxiliaries2_const_ref", [](py::array_t<double> a) {
        const auto &r = a.unchecked<2>();
        const auto &r2 = a.mutable_unchecked<2>();
        return r(0, 0) == r2(0, 0);
    });

358
    // test_array_unchecked_dyn_dims
359
360
361
362
    // Same as the above, but without a compile-time dimensions specification:
    sm.def("proxy_add2_dyn", [](py::array_t<double> a, double v) {
        auto r = a.mutable_unchecked();
        if (r.ndim() != 2) throw std::domain_error("error: ndim != 2");
363
364
        for (py::ssize_t i = 0; i < r.shape(0); i++)
            for (py::ssize_t j = 0; j < r.shape(1); j++)
365
                r(i, j) += v;
366
    }, py::arg{}.noconvert(), py::arg());
367
368
369
370
    sm.def("proxy_init3_dyn", [](double start) {
        py::array_t<double, py::array::c_style> a({ 3, 3, 3 });
        auto r = a.mutable_unchecked();
        if (r.ndim() != 3) throw std::domain_error("error: ndim != 3");
371
372
373
        for (py::ssize_t i = 0; i < r.shape(0); i++)
        for (py::ssize_t j = 0; j < r.shape(1); j++)
        for (py::ssize_t k = 0; k < r.shape(2); k++)
374
375
376
377
378
379
380
381
382
383
            r(i, j, k) = start++;
        return a;
    });
    sm.def("proxy_auxiliaries2_dyn", [](py::array_t<double> a) {
        return auxiliaries(a.unchecked(), a.mutable_unchecked());
    });

    sm.def("array_auxiliaries2", [](py::array_t<double> a) {
        return auxiliaries(a, a);
    });
384

385
    // test_array_failures
386
387
388
    // Issue #785: Uninformative "Unknown internal error" exception when constructing array from empty object:
    sm.def("array_fail_test", []() { return py::array(py::object()); });
    sm.def("array_t_fail_test", []() { return py::array_t<double>(py::object()); });
389
390
391
    // Make sure the error from numpy is being passed through:
    sm.def("array_fail_test_negative_size", []() { int c = 0; return py::array(-1, &c); });

392
    // test_initializer_list
393
    // Issue (unnumbered; reported in #788): regression: initializer lists can be ambiguous
394
395
396
397
    sm.def("array_initializer_list1", []() { return py::array_t<float>(1); }); // { 1 } also works, but clang warns about it
    sm.def("array_initializer_list2", []() { return py::array_t<float>({ 1, 2 }); });
    sm.def("array_initializer_list3", []() { return py::array_t<float>({ 1, 2, 3 }); });
    sm.def("array_initializer_list4", []() { return py::array_t<float>({ 1, 2, 3, 4 }); });
uentity's avatar
uentity committed
398

399
    // test_array_resize
uentity's avatar
uentity committed
400
401
    // reshape array to 2D without changing size
    sm.def("array_reshape2", [](py::array_t<double> a) {
402
        const auto dim_sz = (py::ssize_t)std::sqrt(a.size());
uentity's avatar
uentity committed
403
404
405
406
407
408
409
410
411
412
        if (dim_sz * dim_sz != a.size())
            throw std::domain_error("array_reshape2: input array total size is not a squared integer");
        a.resize({dim_sz, dim_sz});
    });

    // resize to 3D array with each dimension = N
    sm.def("array_resize3", [](py::array_t<double> a, size_t N, bool refcheck) {
        a.resize({N, N, N}, refcheck);
    });

413
    // test_array_create_and_resize
uentity's avatar
uentity committed
414
415
416
417
418
419
420
    // return 2D array with Nrows = Ncols = N
    sm.def("create_and_resize", [](size_t N) {
        py::array_t<double> a;
        a.resize({N, N});
        std::fill(a.mutable_data(), a.mutable_data() + a.size(), 42.);
        return a;
    });
421

422
423
    sm.def("index_using_ellipsis",
           [](const py::array &a) { return a[py::make_tuple(0, py::ellipsis(), 0)]; });
424
425

    // test_argument_conversions
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
    sm.def(
        "accept_double",
        // NOLINTNEXTLINE(performance-unnecessary-value-param)
        [](py::array_t<double, 0>) {},
        py::arg("a"));
    sm.def(
        "accept_double_forcecast",
        // NOLINTNEXTLINE(performance-unnecessary-value-param)
        [](py::array_t<double, py::array::forcecast>) {},
        py::arg("a"));
    sm.def(
        "accept_double_c_style",
        // NOLINTNEXTLINE(performance-unnecessary-value-param)
        [](py::array_t<double, py::array::c_style>) {},
        py::arg("a"));
    sm.def(
        "accept_double_c_style_forcecast",
        // NOLINTNEXTLINE(performance-unnecessary-value-param)
        [](py::array_t<double, py::array::forcecast | py::array::c_style>) {},
        py::arg("a"));
    sm.def(
        "accept_double_f_style",
        // NOLINTNEXTLINE(performance-unnecessary-value-param)
        [](py::array_t<double, py::array::f_style>) {},
        py::arg("a"));
    sm.def(
        "accept_double_f_style_forcecast",
        // NOLINTNEXTLINE(performance-unnecessary-value-param)
        [](py::array_t<double, py::array::forcecast | py::array::f_style>) {},
        py::arg("a"));
    sm.def(
        "accept_double_noconvert",
        // NOLINTNEXTLINE(performance-unnecessary-value-param)
        [](py::array_t<double, 0>) {},
        "a"_a.noconvert());
    sm.def(
        "accept_double_forcecast_noconvert",
        // NOLINTNEXTLINE(performance-unnecessary-value-param)
        [](py::array_t<double, py::array::forcecast>) {},
        "a"_a.noconvert());
    sm.def(
        "accept_double_c_style_noconvert",
        // NOLINTNEXTLINE(performance-unnecessary-value-param)
        [](py::array_t<double, py::array::c_style>) {},
        "a"_a.noconvert());
    sm.def(
        "accept_double_c_style_forcecast_noconvert",
        // NOLINTNEXTLINE(performance-unnecessary-value-param)
        [](py::array_t<double, py::array::forcecast | py::array::c_style>) {},
        "a"_a.noconvert());
    sm.def(
        "accept_double_f_style_noconvert",
        // NOLINTNEXTLINE(performance-unnecessary-value-param)
        [](py::array_t<double, py::array::f_style>) {},
        "a"_a.noconvert());
    sm.def(
        "accept_double_f_style_forcecast_noconvert",
        // NOLINTNEXTLINE(performance-unnecessary-value-param)
        [](py::array_t<double, py::array::forcecast | py::array::f_style>) {},
        "a"_a.noconvert());
486
487

    // Check that types returns correct npy format descriptor
488
    // NOLINTNEXTLINE(performance-unnecessary-value-param)
489
    sm.def("test_fmt_desc_float", [](py::array_t<float>) {});
490
    // NOLINTNEXTLINE(performance-unnecessary-value-param)
491
    sm.def("test_fmt_desc_double", [](py::array_t<double>) {});
492
    // NOLINTNEXTLINE(performance-unnecessary-value-param)
493
    sm.def("test_fmt_desc_const_float", [](py::array_t<const float>) {});
494
    // NOLINTNEXTLINE(performance-unnecessary-value-param)
495
    sm.def("test_fmt_desc_const_double", [](py::array_t<const double>) {});
496
}