numpy.rst revision 14299
111986Sandreas.sandberg@arm.com.. _numpy: 211986Sandreas.sandberg@arm.com 311986Sandreas.sandberg@arm.comNumPy 411986Sandreas.sandberg@arm.com##### 511986Sandreas.sandberg@arm.com 611986Sandreas.sandberg@arm.comBuffer protocol 711986Sandreas.sandberg@arm.com=============== 811986Sandreas.sandberg@arm.com 911986Sandreas.sandberg@arm.comPython supports an extremely general and convenient approach for exchanging 1011986Sandreas.sandberg@arm.comdata between plugin libraries. Types can expose a buffer view [#f2]_, which 1111986Sandreas.sandberg@arm.comprovides fast direct access to the raw internal data representation. Suppose we 1211986Sandreas.sandberg@arm.comwant to bind the following simplistic Matrix class: 1311986Sandreas.sandberg@arm.com 1411986Sandreas.sandberg@arm.com.. code-block:: cpp 1511986Sandreas.sandberg@arm.com 1611986Sandreas.sandberg@arm.com class Matrix { 1711986Sandreas.sandberg@arm.com public: 1811986Sandreas.sandberg@arm.com Matrix(size_t rows, size_t cols) : m_rows(rows), m_cols(cols) { 1911986Sandreas.sandberg@arm.com m_data = new float[rows*cols]; 2011986Sandreas.sandberg@arm.com } 2111986Sandreas.sandberg@arm.com float *data() { return m_data; } 2211986Sandreas.sandberg@arm.com size_t rows() const { return m_rows; } 2311986Sandreas.sandberg@arm.com size_t cols() const { return m_cols; } 2411986Sandreas.sandberg@arm.com private: 2511986Sandreas.sandberg@arm.com size_t m_rows, m_cols; 2611986Sandreas.sandberg@arm.com float *m_data; 2711986Sandreas.sandberg@arm.com }; 2811986Sandreas.sandberg@arm.com 2911986Sandreas.sandberg@arm.comThe following binding code exposes the ``Matrix`` contents as a buffer object, 3011986Sandreas.sandberg@arm.commaking it possible to cast Matrices into NumPy arrays. It is even possible to 3111986Sandreas.sandberg@arm.comcompletely avoid copy operations with Python expressions like 3211986Sandreas.sandberg@arm.com``np.array(matrix_instance, copy = False)``. 3311986Sandreas.sandberg@arm.com 3411986Sandreas.sandberg@arm.com.. code-block:: cpp 3511986Sandreas.sandberg@arm.com 3612037Sandreas.sandberg@arm.com py::class_<Matrix>(m, "Matrix", py::buffer_protocol()) 3711986Sandreas.sandberg@arm.com .def_buffer([](Matrix &m) -> py::buffer_info { 3811986Sandreas.sandberg@arm.com return py::buffer_info( 3911986Sandreas.sandberg@arm.com m.data(), /* Pointer to buffer */ 4011986Sandreas.sandberg@arm.com sizeof(float), /* Size of one scalar */ 4111986Sandreas.sandberg@arm.com py::format_descriptor<float>::format(), /* Python struct-style format descriptor */ 4211986Sandreas.sandberg@arm.com 2, /* Number of dimensions */ 4311986Sandreas.sandberg@arm.com { m.rows(), m.cols() }, /* Buffer dimensions */ 4414299Sbbruce@ucdavis.edu { sizeof(float) * m.cols(), /* Strides (in bytes) for each index */ 4511986Sandreas.sandberg@arm.com sizeof(float) } 4611986Sandreas.sandberg@arm.com ); 4711986Sandreas.sandberg@arm.com }); 4811986Sandreas.sandberg@arm.com 4912037Sandreas.sandberg@arm.comSupporting the buffer protocol in a new type involves specifying the special 5012037Sandreas.sandberg@arm.com``py::buffer_protocol()`` tag in the ``py::class_`` constructor and calling the 5112037Sandreas.sandberg@arm.com``def_buffer()`` method with a lambda function that creates a 5212037Sandreas.sandberg@arm.com``py::buffer_info`` description record on demand describing a given matrix 5312037Sandreas.sandberg@arm.cominstance. The contents of ``py::buffer_info`` mirror the Python buffer protocol 5412037Sandreas.sandberg@arm.comspecification. 5511986Sandreas.sandberg@arm.com 5611986Sandreas.sandberg@arm.com.. code-block:: cpp 5711986Sandreas.sandberg@arm.com 5811986Sandreas.sandberg@arm.com struct buffer_info { 5911986Sandreas.sandberg@arm.com void *ptr; 6012391Sjason@lowepower.com ssize_t itemsize; 6111986Sandreas.sandberg@arm.com std::string format; 6212391Sjason@lowepower.com ssize_t ndim; 6312391Sjason@lowepower.com std::vector<ssize_t> shape; 6412391Sjason@lowepower.com std::vector<ssize_t> strides; 6511986Sandreas.sandberg@arm.com }; 6611986Sandreas.sandberg@arm.com 6711986Sandreas.sandberg@arm.comTo create a C++ function that can take a Python buffer object as an argument, 6811986Sandreas.sandberg@arm.comsimply use the type ``py::buffer`` as one of its arguments. Buffers can exist 6911986Sandreas.sandberg@arm.comin a great variety of configurations, hence some safety checks are usually 7011986Sandreas.sandberg@arm.comnecessary in the function body. Below, you can see an basic example on how to 7111986Sandreas.sandberg@arm.comdefine a custom constructor for the Eigen double precision matrix 7211986Sandreas.sandberg@arm.com(``Eigen::MatrixXd``) type, which supports initialization from compatible 7311986Sandreas.sandberg@arm.combuffer objects (e.g. a NumPy matrix). 7411986Sandreas.sandberg@arm.com 7511986Sandreas.sandberg@arm.com.. code-block:: cpp 7611986Sandreas.sandberg@arm.com 7711986Sandreas.sandberg@arm.com /* Bind MatrixXd (or some other Eigen type) to Python */ 7811986Sandreas.sandberg@arm.com typedef Eigen::MatrixXd Matrix; 7911986Sandreas.sandberg@arm.com 8011986Sandreas.sandberg@arm.com typedef Matrix::Scalar Scalar; 8111986Sandreas.sandberg@arm.com constexpr bool rowMajor = Matrix::Flags & Eigen::RowMajorBit; 8211986Sandreas.sandberg@arm.com 8312037Sandreas.sandberg@arm.com py::class_<Matrix>(m, "Matrix", py::buffer_protocol()) 8411986Sandreas.sandberg@arm.com .def("__init__", [](Matrix &m, py::buffer b) { 8511986Sandreas.sandberg@arm.com typedef Eigen::Stride<Eigen::Dynamic, Eigen::Dynamic> Strides; 8611986Sandreas.sandberg@arm.com 8711986Sandreas.sandberg@arm.com /* Request a buffer descriptor from Python */ 8811986Sandreas.sandberg@arm.com py::buffer_info info = b.request(); 8911986Sandreas.sandberg@arm.com 9011986Sandreas.sandberg@arm.com /* Some sanity checks ... */ 9111986Sandreas.sandberg@arm.com if (info.format != py::format_descriptor<Scalar>::format()) 9211986Sandreas.sandberg@arm.com throw std::runtime_error("Incompatible format: expected a double array!"); 9311986Sandreas.sandberg@arm.com 9411986Sandreas.sandberg@arm.com if (info.ndim != 2) 9511986Sandreas.sandberg@arm.com throw std::runtime_error("Incompatible buffer dimension!"); 9611986Sandreas.sandberg@arm.com 9711986Sandreas.sandberg@arm.com auto strides = Strides( 9812391Sjason@lowepower.com info.strides[rowMajor ? 0 : 1] / (py::ssize_t)sizeof(Scalar), 9912391Sjason@lowepower.com info.strides[rowMajor ? 1 : 0] / (py::ssize_t)sizeof(Scalar)); 10011986Sandreas.sandberg@arm.com 10111986Sandreas.sandberg@arm.com auto map = Eigen::Map<Matrix, 0, Strides>( 10212391Sjason@lowepower.com static_cast<Scalar *>(info.ptr), info.shape[0], info.shape[1], strides); 10311986Sandreas.sandberg@arm.com 10411986Sandreas.sandberg@arm.com new (&m) Matrix(map); 10511986Sandreas.sandberg@arm.com }); 10611986Sandreas.sandberg@arm.com 10711986Sandreas.sandberg@arm.comFor reference, the ``def_buffer()`` call for this Eigen data type should look 10811986Sandreas.sandberg@arm.comas follows: 10911986Sandreas.sandberg@arm.com 11011986Sandreas.sandberg@arm.com.. code-block:: cpp 11111986Sandreas.sandberg@arm.com 11211986Sandreas.sandberg@arm.com .def_buffer([](Matrix &m) -> py::buffer_info { 11311986Sandreas.sandberg@arm.com return py::buffer_info( 11412391Sjason@lowepower.com m.data(), /* Pointer to buffer */ 11512391Sjason@lowepower.com sizeof(Scalar), /* Size of one scalar */ 11612391Sjason@lowepower.com py::format_descriptor<Scalar>::format(), /* Python struct-style format descriptor */ 11712391Sjason@lowepower.com 2, /* Number of dimensions */ 11812391Sjason@lowepower.com { m.rows(), m.cols() }, /* Buffer dimensions */ 11911986Sandreas.sandberg@arm.com { sizeof(Scalar) * (rowMajor ? m.cols() : 1), 12011986Sandreas.sandberg@arm.com sizeof(Scalar) * (rowMajor ? 1 : m.rows()) } 12112391Sjason@lowepower.com /* Strides (in bytes) for each index */ 12211986Sandreas.sandberg@arm.com ); 12311986Sandreas.sandberg@arm.com }) 12411986Sandreas.sandberg@arm.com 12511986Sandreas.sandberg@arm.comFor a much easier approach of binding Eigen types (although with some 12611986Sandreas.sandberg@arm.comlimitations), refer to the section on :doc:`/advanced/cast/eigen`. 12711986Sandreas.sandberg@arm.com 12811986Sandreas.sandberg@arm.com.. seealso:: 12911986Sandreas.sandberg@arm.com 13011986Sandreas.sandberg@arm.com The file :file:`tests/test_buffers.cpp` contains a complete example 13111986Sandreas.sandberg@arm.com that demonstrates using the buffer protocol with pybind11 in more detail. 13211986Sandreas.sandberg@arm.com 13311986Sandreas.sandberg@arm.com.. [#f2] http://docs.python.org/3/c-api/buffer.html 13411986Sandreas.sandberg@arm.com 13511986Sandreas.sandberg@arm.comArrays 13611986Sandreas.sandberg@arm.com====== 13711986Sandreas.sandberg@arm.com 13811986Sandreas.sandberg@arm.comBy exchanging ``py::buffer`` with ``py::array`` in the above snippet, we can 13911986Sandreas.sandberg@arm.comrestrict the function so that it only accepts NumPy arrays (rather than any 14011986Sandreas.sandberg@arm.comtype of Python object satisfying the buffer protocol). 14111986Sandreas.sandberg@arm.com 14211986Sandreas.sandberg@arm.comIn many situations, we want to define a function which only accepts a NumPy 14311986Sandreas.sandberg@arm.comarray of a certain data type. This is possible via the ``py::array_t<T>`` 14411986Sandreas.sandberg@arm.comtemplate. For instance, the following function requires the argument to be a 14511986Sandreas.sandberg@arm.comNumPy array containing double precision values. 14611986Sandreas.sandberg@arm.com 14711986Sandreas.sandberg@arm.com.. code-block:: cpp 14811986Sandreas.sandberg@arm.com 14911986Sandreas.sandberg@arm.com void f(py::array_t<double> array); 15011986Sandreas.sandberg@arm.com 15111986Sandreas.sandberg@arm.comWhen it is invoked with a different type (e.g. an integer or a list of 15211986Sandreas.sandberg@arm.comintegers), the binding code will attempt to cast the input into a NumPy array 15311986Sandreas.sandberg@arm.comof the requested type. Note that this feature requires the 15412037Sandreas.sandberg@arm.com:file:`pybind11/numpy.h` header to be included. 15511986Sandreas.sandberg@arm.com 15611986Sandreas.sandberg@arm.comData in NumPy arrays is not guaranteed to packed in a dense manner; 15711986Sandreas.sandberg@arm.comfurthermore, entries can be separated by arbitrary column and row strides. 15811986Sandreas.sandberg@arm.comSometimes, it can be useful to require a function to only accept dense arrays 15911986Sandreas.sandberg@arm.comusing either the C (row-major) or Fortran (column-major) ordering. This can be 16011986Sandreas.sandberg@arm.comaccomplished via a second template argument with values ``py::array::c_style`` 16111986Sandreas.sandberg@arm.comor ``py::array::f_style``. 16211986Sandreas.sandberg@arm.com 16311986Sandreas.sandberg@arm.com.. code-block:: cpp 16411986Sandreas.sandberg@arm.com 16511986Sandreas.sandberg@arm.com void f(py::array_t<double, py::array::c_style | py::array::forcecast> array); 16611986Sandreas.sandberg@arm.com 16711986Sandreas.sandberg@arm.comThe ``py::array::forcecast`` argument is the default value of the second 16811986Sandreas.sandberg@arm.comtemplate parameter, and it ensures that non-conforming arguments are converted 16911986Sandreas.sandberg@arm.cominto an array satisfying the specified requirements instead of trying the next 17011986Sandreas.sandberg@arm.comfunction overload. 17111986Sandreas.sandberg@arm.com 17211986Sandreas.sandberg@arm.comStructured types 17311986Sandreas.sandberg@arm.com================ 17411986Sandreas.sandberg@arm.com 17512037Sandreas.sandberg@arm.comIn order for ``py::array_t`` to work with structured (record) types, we first 17612037Sandreas.sandberg@arm.comneed to register the memory layout of the type. This can be done via 17712037Sandreas.sandberg@arm.com``PYBIND11_NUMPY_DTYPE`` macro, called in the plugin definition code, which 17812037Sandreas.sandberg@arm.comexpects the type followed by field names: 17911986Sandreas.sandberg@arm.com 18011986Sandreas.sandberg@arm.com.. code-block:: cpp 18111986Sandreas.sandberg@arm.com 18211986Sandreas.sandberg@arm.com struct A { 18311986Sandreas.sandberg@arm.com int x; 18411986Sandreas.sandberg@arm.com double y; 18511986Sandreas.sandberg@arm.com }; 18611986Sandreas.sandberg@arm.com 18711986Sandreas.sandberg@arm.com struct B { 18811986Sandreas.sandberg@arm.com int z; 18911986Sandreas.sandberg@arm.com A a; 19011986Sandreas.sandberg@arm.com }; 19111986Sandreas.sandberg@arm.com 19212037Sandreas.sandberg@arm.com // ... 19312391Sjason@lowepower.com PYBIND11_MODULE(test, m) { 19412037Sandreas.sandberg@arm.com // ... 19511986Sandreas.sandberg@arm.com 19612037Sandreas.sandberg@arm.com PYBIND11_NUMPY_DTYPE(A, x, y); 19712037Sandreas.sandberg@arm.com PYBIND11_NUMPY_DTYPE(B, z, a); 19812037Sandreas.sandberg@arm.com /* now both A and B can be used as template arguments to py::array_t */ 19912037Sandreas.sandberg@arm.com } 20011986Sandreas.sandberg@arm.com 20112391Sjason@lowepower.comThe structure should consist of fundamental arithmetic types, ``std::complex``, 20212391Sjason@lowepower.compreviously registered substructures, and arrays of any of the above. Both C++ 20312391Sjason@lowepower.comarrays and ``std::array`` are supported. While there is a static assertion to 20412391Sjason@lowepower.comprevent many types of unsupported structures, it is still the user's 20512391Sjason@lowepower.comresponsibility to use only "plain" structures that can be safely manipulated as 20612391Sjason@lowepower.comraw memory without violating invariants. 20712391Sjason@lowepower.com 20811986Sandreas.sandberg@arm.comVectorizing functions 20911986Sandreas.sandberg@arm.com===================== 21011986Sandreas.sandberg@arm.com 21111986Sandreas.sandberg@arm.comSuppose we want to bind a function with the following signature to Python so 21211986Sandreas.sandberg@arm.comthat it can process arbitrary NumPy array arguments (vectors, matrices, general 21311986Sandreas.sandberg@arm.comN-D arrays) in addition to its normal arguments: 21411986Sandreas.sandberg@arm.com 21511986Sandreas.sandberg@arm.com.. code-block:: cpp 21611986Sandreas.sandberg@arm.com 21711986Sandreas.sandberg@arm.com double my_func(int x, float y, double z); 21811986Sandreas.sandberg@arm.com 21911986Sandreas.sandberg@arm.comAfter including the ``pybind11/numpy.h`` header, this is extremely simple: 22011986Sandreas.sandberg@arm.com 22111986Sandreas.sandberg@arm.com.. code-block:: cpp 22211986Sandreas.sandberg@arm.com 22311986Sandreas.sandberg@arm.com m.def("vectorized_func", py::vectorize(my_func)); 22411986Sandreas.sandberg@arm.com 22511986Sandreas.sandberg@arm.comInvoking the function like below causes 4 calls to be made to ``my_func`` with 22611986Sandreas.sandberg@arm.comeach of the array elements. The significant advantage of this compared to 22711986Sandreas.sandberg@arm.comsolutions like ``numpy.vectorize()`` is that the loop over the elements runs 22811986Sandreas.sandberg@arm.comentirely on the C++ side and can be crunched down into a tight, optimized loop 22911986Sandreas.sandberg@arm.comby the compiler. The result is returned as a NumPy array of type 23011986Sandreas.sandberg@arm.com``numpy.dtype.float64``. 23111986Sandreas.sandberg@arm.com 23211986Sandreas.sandberg@arm.com.. code-block:: pycon 23311986Sandreas.sandberg@arm.com 23411986Sandreas.sandberg@arm.com >>> x = np.array([[1, 3],[5, 7]]) 23511986Sandreas.sandberg@arm.com >>> y = np.array([[2, 4],[6, 8]]) 23611986Sandreas.sandberg@arm.com >>> z = 3 23711986Sandreas.sandberg@arm.com >>> result = vectorized_func(x, y, z) 23811986Sandreas.sandberg@arm.com 23911986Sandreas.sandberg@arm.comThe scalar argument ``z`` is transparently replicated 4 times. The input 24011986Sandreas.sandberg@arm.comarrays ``x`` and ``y`` are automatically converted into the right types (they 24111986Sandreas.sandberg@arm.comare of type ``numpy.dtype.int64`` but need to be ``numpy.dtype.int32`` and 24212391Sjason@lowepower.com``numpy.dtype.float32``, respectively). 24311986Sandreas.sandberg@arm.com 24412391Sjason@lowepower.com.. note:: 24511986Sandreas.sandberg@arm.com 24612391Sjason@lowepower.com Only arithmetic, complex, and POD types passed by value or by ``const &`` 24712391Sjason@lowepower.com reference are vectorized; all other arguments are passed through as-is. 24812391Sjason@lowepower.com Functions taking rvalue reference arguments cannot be vectorized. 24911986Sandreas.sandberg@arm.com 25011986Sandreas.sandberg@arm.comIn cases where the computation is too complicated to be reduced to 25111986Sandreas.sandberg@arm.com``vectorize``, it will be necessary to create and access the buffer contents 25211986Sandreas.sandberg@arm.commanually. The following snippet contains a complete example that shows how this 25311986Sandreas.sandberg@arm.comworks (the code is somewhat contrived, since it could have been done more 25411986Sandreas.sandberg@arm.comsimply using ``vectorize``). 25511986Sandreas.sandberg@arm.com 25611986Sandreas.sandberg@arm.com.. code-block:: cpp 25711986Sandreas.sandberg@arm.com 25811986Sandreas.sandberg@arm.com #include <pybind11/pybind11.h> 25911986Sandreas.sandberg@arm.com #include <pybind11/numpy.h> 26011986Sandreas.sandberg@arm.com 26111986Sandreas.sandberg@arm.com namespace py = pybind11; 26211986Sandreas.sandberg@arm.com 26311986Sandreas.sandberg@arm.com py::array_t<double> add_arrays(py::array_t<double> input1, py::array_t<double> input2) { 26414299Sbbruce@ucdavis.edu py::buffer_info buf1 = input1.request(), buf2 = input2.request(); 26511986Sandreas.sandberg@arm.com 26611986Sandreas.sandberg@arm.com if (buf1.ndim != 1 || buf2.ndim != 1) 26711986Sandreas.sandberg@arm.com throw std::runtime_error("Number of dimensions must be one"); 26811986Sandreas.sandberg@arm.com 26911986Sandreas.sandberg@arm.com if (buf1.size != buf2.size) 27011986Sandreas.sandberg@arm.com throw std::runtime_error("Input shapes must match"); 27111986Sandreas.sandberg@arm.com 27211986Sandreas.sandberg@arm.com /* No pointer is passed, so NumPy will allocate the buffer */ 27311986Sandreas.sandberg@arm.com auto result = py::array_t<double>(buf1.size); 27411986Sandreas.sandberg@arm.com 27514299Sbbruce@ucdavis.edu py::buffer_info buf3 = result.request(); 27611986Sandreas.sandberg@arm.com 27711986Sandreas.sandberg@arm.com double *ptr1 = (double *) buf1.ptr, 27811986Sandreas.sandberg@arm.com *ptr2 = (double *) buf2.ptr, 27911986Sandreas.sandberg@arm.com *ptr3 = (double *) buf3.ptr; 28011986Sandreas.sandberg@arm.com 28111986Sandreas.sandberg@arm.com for (size_t idx = 0; idx < buf1.shape[0]; idx++) 28211986Sandreas.sandberg@arm.com ptr3[idx] = ptr1[idx] + ptr2[idx]; 28311986Sandreas.sandberg@arm.com 28411986Sandreas.sandberg@arm.com return result; 28511986Sandreas.sandberg@arm.com } 28611986Sandreas.sandberg@arm.com 28712391Sjason@lowepower.com PYBIND11_MODULE(test, m) { 28811986Sandreas.sandberg@arm.com m.def("add_arrays", &add_arrays, "Add two NumPy arrays"); 28911986Sandreas.sandberg@arm.com } 29011986Sandreas.sandberg@arm.com 29111986Sandreas.sandberg@arm.com.. seealso:: 29211986Sandreas.sandberg@arm.com 29311986Sandreas.sandberg@arm.com The file :file:`tests/test_numpy_vectorize.cpp` contains a complete 29411986Sandreas.sandberg@arm.com example that demonstrates using :func:`vectorize` in more detail. 29512037Sandreas.sandberg@arm.com 29612037Sandreas.sandberg@arm.comDirect access 29712037Sandreas.sandberg@arm.com============= 29812037Sandreas.sandberg@arm.com 29912037Sandreas.sandberg@arm.comFor performance reasons, particularly when dealing with very large arrays, it 30012037Sandreas.sandberg@arm.comis often desirable to directly access array elements without internal checking 30112037Sandreas.sandberg@arm.comof dimensions and bounds on every access when indices are known to be already 30212037Sandreas.sandberg@arm.comvalid. To avoid such checks, the ``array`` class and ``array_t<T>`` template 30312037Sandreas.sandberg@arm.comclass offer an unchecked proxy object that can be used for this unchecked 30412037Sandreas.sandberg@arm.comaccess through the ``unchecked<N>`` and ``mutable_unchecked<N>`` methods, 30512037Sandreas.sandberg@arm.comwhere ``N`` gives the required dimensionality of the array: 30612037Sandreas.sandberg@arm.com 30712037Sandreas.sandberg@arm.com.. code-block:: cpp 30812037Sandreas.sandberg@arm.com 30912037Sandreas.sandberg@arm.com m.def("sum_3d", [](py::array_t<double> x) { 31012037Sandreas.sandberg@arm.com auto r = x.unchecked<3>(); // x must have ndim = 3; can be non-writeable 31112037Sandreas.sandberg@arm.com double sum = 0; 31212391Sjason@lowepower.com for (ssize_t i = 0; i < r.shape(0); i++) 31312391Sjason@lowepower.com for (ssize_t j = 0; j < r.shape(1); j++) 31412391Sjason@lowepower.com for (ssize_t k = 0; k < r.shape(2); k++) 31512037Sandreas.sandberg@arm.com sum += r(i, j, k); 31612037Sandreas.sandberg@arm.com return sum; 31712037Sandreas.sandberg@arm.com }); 31812037Sandreas.sandberg@arm.com m.def("increment_3d", [](py::array_t<double> x) { 31912037Sandreas.sandberg@arm.com auto r = x.mutable_unchecked<3>(); // Will throw if ndim != 3 or flags.writeable is false 32012391Sjason@lowepower.com for (ssize_t i = 0; i < r.shape(0); i++) 32112391Sjason@lowepower.com for (ssize_t j = 0; j < r.shape(1); j++) 32212391Sjason@lowepower.com for (ssize_t k = 0; k < r.shape(2); k++) 32312037Sandreas.sandberg@arm.com r(i, j, k) += 1.0; 32412037Sandreas.sandberg@arm.com }, py::arg().noconvert()); 32512037Sandreas.sandberg@arm.com 32612037Sandreas.sandberg@arm.comTo obtain the proxy from an ``array`` object, you must specify both the data 32712037Sandreas.sandberg@arm.comtype and number of dimensions as template arguments, such as ``auto r = 32812037Sandreas.sandberg@arm.commyarray.mutable_unchecked<float, 2>()``. 32912037Sandreas.sandberg@arm.com 33012037Sandreas.sandberg@arm.comIf the number of dimensions is not known at compile time, you can omit the 33112037Sandreas.sandberg@arm.comdimensions template parameter (i.e. calling ``arr_t.unchecked()`` or 33212037Sandreas.sandberg@arm.com``arr.unchecked<T>()``. This will give you a proxy object that works in the 33312037Sandreas.sandberg@arm.comsame way, but results in less optimizable code and thus a small efficiency 33412037Sandreas.sandberg@arm.comloss in tight loops. 33512037Sandreas.sandberg@arm.com 33612037Sandreas.sandberg@arm.comNote that the returned proxy object directly references the array's data, and 33712037Sandreas.sandberg@arm.comonly reads its shape, strides, and writeable flag when constructed. You must 33812037Sandreas.sandberg@arm.comtake care to ensure that the referenced array is not destroyed or reshaped for 33912037Sandreas.sandberg@arm.comthe duration of the returned object, typically by limiting the scope of the 34012037Sandreas.sandberg@arm.comreturned instance. 34112037Sandreas.sandberg@arm.com 34212037Sandreas.sandberg@arm.comThe returned proxy object supports some of the same methods as ``py::array`` so 34312037Sandreas.sandberg@arm.comthat it can be used as a drop-in replacement for some existing, index-checked 34412037Sandreas.sandberg@arm.comuses of ``py::array``: 34512037Sandreas.sandberg@arm.com 34612037Sandreas.sandberg@arm.com- ``r.ndim()`` returns the number of dimensions 34712037Sandreas.sandberg@arm.com 34812037Sandreas.sandberg@arm.com- ``r.data(1, 2, ...)`` and ``r.mutable_data(1, 2, ...)``` returns a pointer to 34912037Sandreas.sandberg@arm.com the ``const T`` or ``T`` data, respectively, at the given indices. The 35012037Sandreas.sandberg@arm.com latter is only available to proxies obtained via ``a.mutable_unchecked()``. 35112037Sandreas.sandberg@arm.com 35212037Sandreas.sandberg@arm.com- ``itemsize()`` returns the size of an item in bytes, i.e. ``sizeof(T)``. 35312037Sandreas.sandberg@arm.com 35412037Sandreas.sandberg@arm.com- ``ndim()`` returns the number of dimensions. 35512037Sandreas.sandberg@arm.com 35612037Sandreas.sandberg@arm.com- ``shape(n)`` returns the size of dimension ``n`` 35712037Sandreas.sandberg@arm.com 35812037Sandreas.sandberg@arm.com- ``size()`` returns the total number of elements (i.e. the product of the shapes). 35912037Sandreas.sandberg@arm.com 36012037Sandreas.sandberg@arm.com- ``nbytes()`` returns the number of bytes used by the referenced elements 36112037Sandreas.sandberg@arm.com (i.e. ``itemsize()`` times ``size()``). 36212037Sandreas.sandberg@arm.com 36312037Sandreas.sandberg@arm.com.. seealso:: 36412037Sandreas.sandberg@arm.com 36512037Sandreas.sandberg@arm.com The file :file:`tests/test_numpy_array.cpp` contains additional examples 36612037Sandreas.sandberg@arm.com demonstrating the use of this feature. 36714299Sbbruce@ucdavis.edu 36814299Sbbruce@ucdavis.eduEllipsis 36914299Sbbruce@ucdavis.edu======== 37014299Sbbruce@ucdavis.edu 37114299Sbbruce@ucdavis.eduPython 3 provides a convenient ``...`` ellipsis notation that is often used to 37214299Sbbruce@ucdavis.eduslice multidimensional arrays. For instance, the following snippet extracts the 37314299Sbbruce@ucdavis.edumiddle dimensions of a tensor with the first and last index set to zero. 37414299Sbbruce@ucdavis.edu 37514299Sbbruce@ucdavis.edu.. code-block:: python 37614299Sbbruce@ucdavis.edu 37714299Sbbruce@ucdavis.edu a = # a NumPy array 37814299Sbbruce@ucdavis.edu b = a[0, ..., 0] 37914299Sbbruce@ucdavis.edu 38014299Sbbruce@ucdavis.eduThe function ``py::ellipsis()`` function can be used to perform the same 38114299Sbbruce@ucdavis.eduoperation on the C++ side: 38214299Sbbruce@ucdavis.edu 38314299Sbbruce@ucdavis.edu.. code-block:: cpp 38414299Sbbruce@ucdavis.edu 38514299Sbbruce@ucdavis.edu py::array a = /* A NumPy array */; 38614299Sbbruce@ucdavis.edu py::array b = a[py::make_tuple(0, py::ellipsis(), 0)]; 387