numpy.rst revision 12391
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 */
4411986Sandreas.sandberg@arm.com                { sizeof(float) * m.rows(),             /* 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) {
26411986Sandreas.sandberg@arm.com        auto 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
27511986Sandreas.sandberg@arm.com        auto 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.
367