{
  "__type": "IngestedDoc",
  "__tag": 4010,
  "_content": {},
  "_ordered_sections": [],
  "item_file": null,
  "item_line": null,
  "item_type": null,
  "aliases": [],
  "example_section_data": {
    "__type": "Section",
    "__tag": 4015,
    "children": [],
    "title": [],
    "level": 0,
    "target": null
  },
  "see_also": [],
  "signature": null,
  "references": null,
  "qa": "user:basics.interoperability",
  "arbitrary": [
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "NumPy's ndarray objects provide both a high-level API for operations on array-structured data and a concrete implementation of the API based on "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "strided in-RAM storage",
              "reference": {
                "__type": "LocalRef",
                "__tag": 4022,
                "kind": "docs",
                "path": "reference:arrays"
              },
              "kind": "exists"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ". While this API is powerful and fairly general, its concrete implementation has limitations. As datasets grow and NumPy becomes used in a variety of new environments and architectures, there are cases where the strided in-RAM storage strategy is inappropriate, which has caused different libraries to reimplement this API for their own uses. This includes GPU arrays ("
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "CuPy"
                }
              ],
              "url": "https://cupy.dev/",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "), Sparse arrays ("
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "scipy.sparse",
              "reference": {
                "__type": "RefInfo",
                "__tag": 4000,
                "module": "scipy",
                "version": "*",
                "kind": "api",
                "path": "scipy.sparse"
              },
              "kind": "module"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "PyData/Sparse"
                }
              ],
              "url": "https://sparse.pydata.org/",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ") and parallel arrays ("
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "Dask"
                }
              ],
              "url": "https://docs.dask.org/",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " arrays) as well as various NumPy-like implementations in deep learning frameworks, like "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "TensorFlow"
                }
              ],
              "url": "https://www.tensorflow.org/",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "PyTorch"
                }
              ],
              "url": "https://pytorch.org/",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ". Similarly, there are many projects that build on top of the NumPy API for labeled and indexed arrays ("
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "XArray"
                }
              ],
              "url": "https://xarray.dev/",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "), automatic differentiation ("
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "JAX"
                }
              ],
              "url": "https://jax.readthedocs.io/",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "), masked arrays ("
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "numpy.ma",
              "reference": {
                "__type": "RefInfo",
                "__tag": 4000,
                "module": "numpy",
                "version": "*",
                "kind": "api",
                "path": "numpy.ma"
              },
              "kind": "module"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "), physical units ("
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "astropy.units"
                }
              ],
              "url": "https://docs.astropy.org/en/stable/units/",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "pint"
                }
              ],
              "url": "https://pint.readthedocs.io/",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "unyt"
                }
              ],
              "url": "https://unyt.readthedocs.io/",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "), among others that add additional functionality on top of the NumPy API."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Yet, users still want to work with these arrays using the familiar NumPy API and reuse existing code with minimal (ideally zero) porting overhead. With this goal in mind, various protocols are defined for implementations of multi-dimensional arrays with high-level APIs matching NumPy."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Broadly speaking, there are three groups of features used for interoperability with NumPy:"
            }
          ]
        },
        {
          "__type": "BulletList",
          "__tag": 4053,
          "ordered": true,
          "start": 1,
          "children": [
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "Methods of turning a foreign object into an ndarray;"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "Methods of deferring execution from a NumPy function to another array    library;"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "Methods that use NumPy functions and return an instance of a foreign object."
                    }
                  ]
                }
              ]
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "We describe these features below."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Interoperability with NumPy"
        }
      ],
      "level": 0,
      "target": "basics.interoperability"
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "The first set of interoperability features from the NumPy API allows foreign objects to be treated as NumPy arrays whenever possible. When NumPy functions encounter a foreign object, they will try (in order):"
            }
          ]
        },
        {
          "__type": "BulletList",
          "__tag": 4053,
          "ordered": true,
          "start": 1,
          "children": [
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "The buffer protocol, described "
                    },
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "in the Python C-API documentation",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "docs",
                        "path": "python:c-api/buffer"
                      },
                      "kind": "docs"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "."
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "The "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "__array_interface__"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " protocol, described    "
                    },
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "in this page",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "docs",
                        "path": "reference:arrays.interface"
                      },
                      "kind": "exists"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": ". A precursor to Python's buffer    protocol, it defines a way to access the contents of a NumPy array from other    C extensions."
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "The "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "__array__()"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " method, which asks an arbitrary object to convert    itself into an array."
                    }
                  ]
                }
              ]
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "For both the buffer and the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array_interface__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " protocols, the object describes its memory layout and NumPy does everything else (zero-copy if possible). If that's not possible, the object itself is responsible for returning a "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "ndarray"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " from "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array__()"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "DLPack",
              "reference": {
                "__type": "LocalRef",
                "__tag": 4022,
                "kind": "docs",
                "path": "dlpack:index"
              },
              "kind": "docs"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " is yet another protocol to convert foreign objects to NumPy arrays in a language and device agnostic manner. NumPy doesn't implicitly convert objects to ndarrays using DLPack. It provides the function "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "numpy.from_dlpack",
              "reference": {
                "__type": "RefInfo",
                "__tag": 4000,
                "module": "numpy",
                "version": "*",
                "kind": "api",
                "path": "numpy:from_dlpack"
              },
              "kind": "module"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " that accepts any object implementing the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__dlpack__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " method and outputs a NumPy ndarray (which is generally a view of the input object's data buffer). The "
            },
            {
              "__type": "InlineRole",
              "__tag": 4003,
              "value": "dlpack:python-spec",
              "domain": null,
              "role": "ref",
              "inventory": null
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " page explains the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__dlpack__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " protocol in detail."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "1. Using arbitrary objects in NumPy"
        }
      ],
      "level": 1,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Similar to "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array__()"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " for array objects, defining "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__numpy_dtype__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " allows a custom dtype object to be interoperable with NumPy. The "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__numpy_dtype__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " must return a NumPy dtype instance (note that "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "np.float64"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " is not a dtype instance, "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "np.dtype(np.float64)"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " is)."
            }
          ]
        },
        {
          "__type": "Admonition",
          "__tag": 4056,
          "kind": "versionadded",
          "base_type": "neutral",
          "children": [
            {
              "__type": "AdmonitionTitle",
              "__tag": 4055,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "versionadded 2.4"
                }
              ]
            },
            {
              "__type": "Paragraph",
              "__tag": 4045,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "Before NumPy 2.4 a "
                },
                {
                  "__type": "InlineCode",
                  "__tag": 4051,
                  "value": ".dtype"
                },
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": " attribute was treated similarly. As of NumPy 2.4 both is accepted and implementing "
                },
                {
                  "__type": "InlineCode",
                  "__tag": 4051,
                  "value": "__numpy_dtype__"
                },
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": " prevents "
                },
                {
                  "__type": "InlineCode",
                  "__tag": 4051,
                  "value": ".dtype"
                },
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": " from being checked."
                }
              ]
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "InlineCode",
          "__tag": 4051,
          "value": "dtype"
        },
        {
          "__type": "Text",
          "__tag": 4046,
          "value": " interoperability"
        }
      ],
      "level": 2,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "The "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "array interface protocol",
              "reference": {
                "__type": "LocalRef",
                "__tag": 4022,
                "kind": "docs",
                "path": "reference:arrays.interface"
              },
              "kind": "exists"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " defines a way for array-like objects to reuse each other's data buffers. Its implementation relies on the existence of the following attributes or methods:"
            }
          ]
        },
        {
          "__type": "BulletList",
          "__tag": 4053,
          "ordered": false,
          "start": 1,
          "children": [
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "__array_interface__"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": ": a Python dictionary containing the shape, the    element type, and optionally, the data buffer address and the strides of an    array-like object;"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "__array__()"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": ": a method returning the NumPy ndarray copy or a view of an    array-like object;"
                    }
                  ]
                }
              ]
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "The "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array_interface__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " attribute can be inspected directly:"
            }
          ]
        },
        {
          "__type": "Blockquote",
          "__tag": 4059,
          "children": [
            {
              "__type": "Code",
              "__tag": 4050,
              "value": ">>> import numpy as np\n>>> x = np.array([1, 2, 5.0, 8])\n>>> x.__array_interface__\n{'data': (94708397920832, False), 'strides': None, 'descr': [('', '<f8')], 'typestr': '<f8', 'shape': (4,), 'version': 3}",
              "execution_status": null
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "The "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array_interface__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " attribute can also be used to manipulate the object data in place:"
            }
          ]
        },
        {
          "__type": "Blockquote",
          "__tag": 4059,
          "children": [
            {
              "__type": "Code",
              "__tag": 4050,
              "value": ">>> class wrapper():\n...     pass\n...\n>>> arr = np.array([1, 2, 3, 4])\n>>> buf = arr.__array_interface__\n>>> buf\n{'data': (140497590272032, False), 'strides': None, 'descr': [('', '<i8')], 'typestr': '<i8', 'shape': (4,), 'version': 3}\n>>> buf['shape'] = (2, 2)\n>>> w = wrapper()\n>>> w.__array_interface__ = buf\n>>> new_arr = np.array(w, copy=False)\n>>> new_arr\narray([[1, 2],\n       [3, 4]])",
              "execution_status": null
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "We can check that "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "arr"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "new_arr"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " share the same data buffer:"
            }
          ]
        },
        {
          "__type": "Blockquote",
          "__tag": 4059,
          "children": [
            {
              "__type": "Code",
              "__tag": 4050,
              "value": ">>> new_arr[0, 0] = 1000\n>>> new_arr\narray([[1000,    2],\n       [   3,    4]])\n>>> arr\narray([1000, 2, 3, 4])",
              "execution_status": null
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "The array interface protocol"
        }
      ],
      "level": 2,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "The "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array__()"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " method ensures that any NumPy-like object (an array, any object exposing the array interface, an object whose "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array__()"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " method returns an array or any nested sequence) that implements it can be used as a NumPy array. If possible, this will mean using "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array__()"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " to create a NumPy ndarray view of the array-like object. Otherwise, this copies the data into a new ndarray object. This is not optimal, as coercing arrays into ndarrays may cause performance problems or create the need for copies and loss of metadata, as the original object and any attributes/behavior it may have had, is lost."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "The signature of the method should be "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array__(self, dtype=None, copy=None)"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ". If a passed "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "dtype"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " isn't "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "None"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and different than the object's data type, a casting should happen to a specified type. If "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "copy"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " is "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "None"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", a copy should be made only if "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "dtype"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " argument enforces it. For "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "copy=True"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", a copy should always be made, where "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "copy=False"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " should raise an exception if a copy is needed."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "If a class implements the old signature "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array__(self)"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", for "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "np.array(a)"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " a warning will be raised saying that "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "dtype"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "copy"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " arguments are missing."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "To see an example of a custom array implementation including the use of "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array__()"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", see "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "basics.dispatch",
              "reference": {
                "__type": "LocalRef",
                "__tag": 4022,
                "kind": "docs",
                "path": "user:basics.dispatch"
              },
              "kind": "exists"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "The "
        },
        {
          "__type": "InlineCode",
          "__tag": 4051,
          "value": "__array__()"
        },
        {
          "__type": "Text",
          "__tag": 4046,
          "value": " method"
        }
      ],
      "level": 2,
      "target": "dunder_array.interface"
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "The "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "DLPack",
              "reference": {
                "__type": "LocalRef",
                "__tag": 4022,
                "kind": "docs",
                "path": "dlpack:index"
              },
              "kind": "docs"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " protocol defines a memory-layout of strided n-dimensional array objects. It offers the following syntax for data exchange:"
            }
          ]
        },
        {
          "__type": "BulletList",
          "__tag": 4053,
          "ordered": true,
          "start": 1,
          "children": [
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "A "
                    },
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "numpy.from_dlpack",
                      "reference": {
                        "__type": "RefInfo",
                        "__tag": 4000,
                        "module": "numpy",
                        "version": "*",
                        "kind": "api",
                        "path": "numpy:from_dlpack"
                      },
                      "kind": "module"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " function, which accepts (array) objects with a    "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "__dlpack__"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " method and uses that method to construct a new array    containing the data from "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "x"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "."
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "__dlpack__(self, stream=None)"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " and "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "__dlpack_device__"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " methods on the    array object, which will be called from within "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "from_dlpack"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": ", to query    what device the array is on (may be needed to pass in the correct    stream, e.g. in the case of multiple GPUs) and to access the data."
                    }
                  ]
                }
              ]
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Unlike the buffer protocol, DLPack allows exchanging arrays containing data on devices other than the CPU (e.g. Vulkan or GPU). Since NumPy only supports CPU, it can only convert objects whose data exists on the CPU. But other libraries, like "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "PyTorch"
                }
              ],
              "url": "https://pytorch.org/",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "CuPy"
                }
              ],
              "url": "https://cupy.dev/",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", may exchange data on GPU using this protocol."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "The DLPack Protocol"
        }
      ],
      "level": 2,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "A second set of methods defined by the NumPy API allows us to defer the execution from a NumPy function to another array library."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Consider the following function."
            }
          ]
        },
        {
          "__type": "Blockquote",
          "__tag": 4059,
          "children": [
            {
              "__type": "Code",
              "__tag": 4050,
              "value": ">>> import numpy as np\n>>> def f(x):\n...     return np.mean(np.exp(x))",
              "execution_status": null
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Note that "
            },
            {
              "__type": "InlineRole",
              "__tag": 4003,
              "value": "np.exp <numpy.exp>",
              "domain": null,
              "role": null,
              "inventory": null
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " is a "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "ufunc",
              "reference": {
                "__type": "LocalRef",
                "__tag": 4022,
                "kind": "docs",
                "path": "user:basics.ufuncs"
              },
              "kind": "exists"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", which means that it operates on ndarrays in an element-by-element fashion. On the other hand, "
            },
            {
              "__type": "InlineRole",
              "__tag": 4003,
              "value": "np.mean <numpy.mean>",
              "domain": null,
              "role": null,
              "inventory": null
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " operates along one of the array's axes."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "We can apply "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "f"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " to a NumPy ndarray object directly:"
            }
          ]
        },
        {
          "__type": "Blockquote",
          "__tag": 4059,
          "children": [
            {
              "__type": "Code",
              "__tag": 4050,
              "value": ">>> x = np.array([1, 2, 3, 4])\n>>> f(x)\n21.1977562209304",
              "execution_status": null
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "We would like this function to work equally well with any NumPy-like array object."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "NumPy allows a class to indicate that it would like to handle computations in a custom-defined way through the following interfaces:"
            }
          ]
        },
        {
          "__type": "BulletList",
          "__tag": 4053,
          "ordered": false,
          "start": 1,
          "children": [
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "__array_ufunc__"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": ": allows third-party objects to support and override    "
                    },
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "ufuncs",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "docs",
                        "path": "user:basics.ufuncs"
                      },
                      "kind": "exists"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "."
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "__array_function__"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": ": a catch-all for NumPy functionality that is not    covered by the "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "__array_ufunc__"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " protocol for universal functions."
                    }
                  ]
                }
              ]
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "As long as foreign objects implement the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array_ufunc__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " or "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array_function__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " protocols, it is possible to operate on them without the need for explicit conversion."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "2. Operating on foreign objects without converting"
        }
      ],
      "level": 1,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "A "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "universal function (or ufunc for short)",
              "reference": {
                "__type": "LocalRef",
                "__tag": 4022,
                "kind": "docs",
                "path": "user:basics.ufuncs"
              },
              "kind": "exists"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " is a “vectorized” wrapper for a function that takes a fixed number of specific inputs and produces a fixed number of specific outputs. The output of the ufunc (and its methods) is not necessarily a ndarray, if not all input arguments are ndarrays. Indeed, if any input defines an "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array_ufunc__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " method, control will be passed completely to that function, i.e., the ufunc is overridden. The "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array_ufunc__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " method defined on that (non-ndarray) object has access to the NumPy ufunc. Because ufuncs have a well-defined structure, the foreign "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array_ufunc__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " method may rely on ufunc attributes like "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": ".at()"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": ".reduce()"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", and others."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "A subclass can override what happens when executing NumPy ufuncs on it by overriding the default "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "ndarray.__array_ufunc__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " method. This method is executed instead of the ufunc and should return either the result of the operation, or "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "NotImplemented"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " if the operation requested is not implemented."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "The "
        },
        {
          "__type": "InlineCode",
          "__tag": 4051,
          "value": "__array_ufunc__"
        },
        {
          "__type": "Text",
          "__tag": 4046,
          "value": " protocol"
        }
      ],
      "level": 2,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "To achieve enough coverage of the NumPy API to support downstream projects, there is a need to go beyond "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array_ufunc__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and implement a protocol that allows arguments of a NumPy function to take control and divert execution to another function (for example, a GPU or parallel implementation) in a way that is safe and consistent across projects."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "The semantics of "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array_function__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " are very similar to "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array_ufunc__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", except the operation is specified by an arbitrary callable object rather than a ufunc instance and method. For more details, see "
            },
            {
              "__type": "InlineRole",
              "__tag": 4003,
              "value": "NEP18",
              "domain": null,
              "role": "ref",
              "inventory": null
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "The "
        },
        {
          "__type": "InlineCode",
          "__tag": 4051,
          "value": "__array_function__"
        },
        {
          "__type": "Text",
          "__tag": 4046,
          "value": " protocol"
        }
      ],
      "level": 2,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "A third type of feature set is meant to use the NumPy function implementation and then convert the return value back into an instance of the foreign object. The "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array_finalize__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array_wrap__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " methods act behind the scenes to ensure that the return type of a NumPy function can be specified as needed."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "The "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array_finalize__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " method is the mechanism that NumPy provides to allow subclasses to handle the various ways that new instances get created. This method is called whenever the system internally allocates a new array from an object which is a subclass (subtype) of the ndarray. It can be used to change attributes after construction, or to update meta-information from the “parent.”"
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "The "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array_wrap__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " method “wraps up the action” in the sense of allowing any object (such as user-defined functions) to set the type of its return value and update attributes and metadata. This can be seen as the opposite of the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " method. At the end of every object that implements "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array_wrap__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", this method is called on the input object with the highest "
            },
            {
              "__type": "Emphasis",
              "__tag": 4047,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "array priority"
                }
              ]
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", or the output object if one was specified. The "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array_priority__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " attribute is used to determine what type of object to return in situations where there is more than one possibility for the Python type of the returned object. For example, subclasses may opt to use this method to transform the output array into an instance of the subclass and update metadata before returning the array to the user."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "For more information on these methods, see "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "basics.subclassing",
              "reference": {
                "__type": "LocalRef",
                "__tag": 4022,
                "kind": "docs",
                "path": "user:basics.subclassing"
              },
              "kind": "exists"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "specific-array-subtyping",
              "reference": {
                "__type": "LocalRef",
                "__tag": 4022,
                "kind": "docs",
                "path": "user:c-info.beyond-basics"
              },
              "kind": "exists"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "3. Returning foreign objects"
        }
      ],
      "level": 1,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Interoperability examples"
        }
      ],
      "level": 1,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Consider the following:"
            }
          ]
        },
        {
          "__type": "Blockquote",
          "__tag": 4059,
          "children": [
            {
              "__type": "Code",
              "__tag": 4050,
              "value": ">>> import pandas as pd\n>>> ser = pd.Series([1, 2, 3, 4])\n>>> type(ser)\npandas.core.series.Series",
              "execution_status": null
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Now, "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "ser"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " is "
            },
            {
              "__type": "Strong",
              "__tag": 4048,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "not"
                }
              ]
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " a ndarray, but because it "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "implements the __array_ufunc__ protocol"
                }
              ],
              "url": "https://pandas.pydata.org/docs/user_guide/dsintro.html#dataframe-interoperability-with-numpy-functions",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", we can apply ufuncs to it as if it were a ndarray:"
            }
          ]
        },
        {
          "__type": "Blockquote",
          "__tag": 4059,
          "children": [
            {
              "__type": "Code",
              "__tag": 4050,
              "value": ">>> np.exp(ser)\n   0     2.718282\n   1     7.389056\n   2    20.085537\n   3    54.598150\n   dtype: float64\n>>> np.sin(ser)\n   0    0.841471\n   1    0.909297\n   2    0.141120\n   3   -0.756802\n   dtype: float64",
              "execution_status": null
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "We can even do operations with other ndarrays:"
            }
          ]
        },
        {
          "__type": "Blockquote",
          "__tag": 4059,
          "children": [
            {
              "__type": "Code",
              "__tag": 4050,
              "value": ">>> np.add(ser, np.array([5, 6, 7, 8]))\n   0     6\n   1     8\n   2    10\n   3    12\n   dtype: int64\n>>> f(ser)\n21.1977562209304\n>>> result = ser.__array__()\n>>> type(result)\nnumpy.ndarray",
              "execution_status": null
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Example: Pandas "
        },
        {
          "__type": "InlineCode",
          "__tag": 4051,
          "value": "Series"
        },
        {
          "__type": "Text",
          "__tag": 4046,
          "value": " objects"
        }
      ],
      "level": 2,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "PyTorch"
                }
              ],
              "url": "https://pytorch.org/",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " is an optimized tensor library for deep learning using GPUs and CPUs. PyTorch arrays are commonly called "
            },
            {
              "__type": "Emphasis",
              "__tag": 4047,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "tensors"
                }
              ]
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ". Tensors are similar to NumPy's ndarrays, except that tensors can run on GPUs or other hardware accelerators. In fact, tensors and NumPy arrays can often share the same underlying memory, eliminating the need to copy data."
            }
          ]
        },
        {
          "__type": "Blockquote",
          "__tag": 4059,
          "children": [
            {
              "__type": "Code",
              "__tag": 4050,
              "value": ">>> import torch\n>>> data = [[1, 2],[3, 4]]\n>>> x_np = np.array(data)\n>>> x_tensor = torch.tensor(data)",
              "execution_status": null
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Note that "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "x_np"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "x_tensor"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " are different kinds of objects:"
            }
          ]
        },
        {
          "__type": "Blockquote",
          "__tag": 4059,
          "children": [
            {
              "__type": "Code",
              "__tag": 4050,
              "value": ">>> x_np\narray([[1, 2],\n       [3, 4]])\n>>> x_tensor\ntensor([[1, 2],\n        [3, 4]])",
              "execution_status": null
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "However, we can treat PyTorch tensors as NumPy arrays without the need for explicit conversion:"
            }
          ]
        },
        {
          "__type": "Blockquote",
          "__tag": 4059,
          "children": [
            {
              "__type": "Code",
              "__tag": 4050,
              "value": ">>> np.exp(x_tensor)\ntensor([[ 2.7183,  7.3891],\n        [20.0855, 54.5982]], dtype=torch.float64)",
              "execution_status": null
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Also, note that the return type of this function is compatible with the initial data type."
            }
          ]
        },
        {
          "__type": "Admonition",
          "__tag": 4056,
          "kind": "admonition",
          "base_type": "note",
          "children": [
            {
              "__type": "AdmonitionTitle",
              "__tag": 4055,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "Warning"
                }
              ]
            },
            {
              "__type": "Paragraph",
              "__tag": 4045,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "While this mixing of ndarrays and tensors may be convenient, it is not recommended. It will not work for non-CPU tensors, and will have unexpected behavior in corner cases. Users should prefer explicitly converting the ndarray to a tensor."
                }
              ]
            }
          ]
        },
        {
          "__type": "Admonition",
          "__tag": 4056,
          "kind": "note",
          "base_type": "note",
          "children": [
            {
              "__type": "AdmonitionTitle",
              "__tag": 4055,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "note "
                }
              ]
            },
            {
              "__type": "Paragraph",
              "__tag": 4045,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "PyTorch does not implement "
                },
                {
                  "__type": "InlineCode",
                  "__tag": 4051,
                  "value": "__array_function__"
                },
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": " or "
                },
                {
                  "__type": "InlineCode",
                  "__tag": 4051,
                  "value": "__array_ufunc__"
                },
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": ". Under the hood, the "
                },
                {
                  "__type": "InlineCode",
                  "__tag": 4051,
                  "value": "Tensor.__array__()"
                },
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": " method returns a NumPy ndarray as a view of the tensor data buffer. See "
                },
                {
                  "__type": "Link",
                  "__tag": 4049,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "this issue"
                    }
                  ],
                  "url": "https://github.com/pytorch/pytorch/issues/24015",
                  "title": ""
                },
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": " and the "
                },
                {
                  "__type": "Link",
                  "__tag": 4049,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "__torch_function__ implementation"
                    }
                  ],
                  "url": "https://github.com/pytorch/pytorch/blob/master/torch/overrides.py",
                  "title": ""
                },
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": " for details."
                }
              ]
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Note also that we can see "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array_wrap__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " in action here, even though "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "torch.Tensor"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " is not a subclass of ndarray     "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": ">>> import torch\n>>> t = torch.arange(4)\n>>> np.abs(t)\ntensor([0, 1, 2, 3])",
          "execution_status": null
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "PyTorch implements "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array_wrap__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " to be able to get tensors back from NumPy functions, and we can modify it directly to control which type of objects are returned from these functions."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Example: PyTorch tensors"
        }
      ],
      "level": 2,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "CuPy is a NumPy/SciPy-compatible array library for GPU-accelerated computing with Python. CuPy implements a subset of the NumPy interface by implementing "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "cupy.ndarray"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "a counterpart to NumPy ndarrays"
                }
              ],
              "url": "https://docs.cupy.dev/en/stable/reference/ndarray.html",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "."
            }
          ]
        },
        {
          "__type": "Blockquote",
          "__tag": 4059,
          "children": [
            {
              "__type": "Code",
              "__tag": 4050,
              "value": ">>> import cupy as cp\n>>> x_gpu = cp.array([1, 2, 3, 4])",
              "execution_status": null
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "The "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "cupy.ndarray"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " object implements the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array_ufunc__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " interface. This enables NumPy ufuncs to be applied to CuPy arrays (this will defer operation to the matching CuPy CUDA/ROCm implementation of the ufunc):"
            }
          ]
        },
        {
          "__type": "Blockquote",
          "__tag": 4059,
          "children": [
            {
              "__type": "Code",
              "__tag": 4050,
              "value": ">>> np.mean(np.exp(x_gpu))\narray(21.19775622)",
              "execution_status": null
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Note that the return type of these operations is still consistent with the initial type:"
            }
          ]
        },
        {
          "__type": "Blockquote",
          "__tag": 4059,
          "children": [
            {
              "__type": "Code",
              "__tag": 4050,
              "value": ">>> arr = cp.random.randn(1, 2, 3, 4).astype(cp.float32)\n>>> result = np.sum(arr)\n>>> print(type(result))\n<class 'cupy._core.core.ndarray'>",
              "execution_status": null
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "See "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "this page in the CuPy documentation for details"
                }
              ],
              "url": "https://docs.cupy.dev/en/stable/reference/ufunc.html",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "cupy.ndarray"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " also implements the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array_function__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " interface, meaning it is possible to do operations such as"
            }
          ]
        },
        {
          "__type": "Blockquote",
          "__tag": 4059,
          "children": [
            {
              "__type": "Code",
              "__tag": 4050,
              "value": ">>> a = np.random.randn(100, 100)\n>>> a_gpu = cp.asarray(a)\n>>> qr_gpu = np.linalg.qr(a_gpu)",
              "execution_status": null
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "CuPy implements many NumPy functions on "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "cupy.ndarray"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " objects, but not all. See "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "the CuPy documentation"
                }
              ],
              "url": "https://docs.cupy.dev/en/stable/user_guide/difference.html",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " for details."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Example: CuPy arrays"
        }
      ],
      "level": 2,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Dask is a flexible library for parallel computing in Python. Dask Array implements a subset of the NumPy ndarray interface using blocked algorithms, cutting up the large array into many small arrays. This allows computations on larger-than-memory arrays using multiple cores."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Dask supports "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array__()"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array_ufunc__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "."
            }
          ]
        },
        {
          "__type": "Blockquote",
          "__tag": 4059,
          "children": [
            {
              "__type": "Code",
              "__tag": 4050,
              "value": ">>> import dask.array as da\n>>> x = da.random.normal(1, 0.1, size=(20, 20), chunks=(10, 10))\n>>> np.mean(np.exp(x))\ndask.array<mean_agg-aggregate, shape=(), dtype=float64, chunksize=(), chunktype=numpy.ndarray>\n>>> np.mean(np.exp(x)).compute()\n5.090097550553843",
              "execution_status": null
            }
          ]
        },
        {
          "__type": "Admonition",
          "__tag": 4056,
          "kind": "note",
          "base_type": "note",
          "children": [
            {
              "__type": "AdmonitionTitle",
              "__tag": 4055,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "note "
                }
              ]
            },
            {
              "__type": "Paragraph",
              "__tag": 4045,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "Dask is lazily evaluated, and the result from a computation isn't computed until you ask for it by invoking "
                },
                {
                  "__type": "InlineCode",
                  "__tag": 4051,
                  "value": "compute()"
                },
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "."
                }
              ]
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "See "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "the Dask array documentation"
                }
              ],
              "url": "https://docs.dask.org/en/stable/array.html",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and the "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "scope of Dask arrays interoperability with NumPy arrays"
                }
              ],
              "url": "https://docs.dask.org/en/stable/array.html#scope",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " for details."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Example: Dask arrays"
        }
      ],
      "level": 2,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Several Python data science libraries implement the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__dlpack__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " protocol. Among them are "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "PyTorch"
                }
              ],
              "url": "https://pytorch.org/",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "CuPy"
                }
              ],
              "url": "https://cupy.dev/",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ". A full list of libraries that implement this protocol can be found on "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "this page of DLPack documentation",
              "reference": {
                "__type": "LocalRef",
                "__tag": 4022,
                "kind": "docs",
                "path": "dlpack:index"
              },
              "kind": "docs"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Convert a PyTorch CPU tensor to NumPy array:"
            }
          ]
        },
        {
          "__type": "Blockquote",
          "__tag": 4059,
          "children": [
            {
              "__type": "Code",
              "__tag": 4050,
              "value": ">>> import torch\n>>> x_torch = torch.arange(5)\n>>> x_torch\ntensor([0, 1, 2, 3, 4])\n>>> x_np = np.from_dlpack(x_torch)\n>>> x_np\narray([0, 1, 2, 3, 4])\n>>> # note that x_np is a view of x_torch\n>>> x_torch[1] = 100\n>>> x_torch\ntensor([  0, 100,   2,   3,   4])\n>>> x_np\narray([  0, 100,   2,   3,   4])",
              "execution_status": null
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "The imported arrays are read-only so writing or operating in-place will fail:"
            }
          ]
        },
        {
          "__type": "Blockquote",
          "__tag": 4059,
          "children": [
            {
              "__type": "Code",
              "__tag": 4050,
              "value": ">>> x.flags.writeable\nFalse\n>>> x_np[1] = 1\nTraceback (most recent call last):\n  File \"<stdin>\", line 1, in <module>\nValueError: assignment destination is read-only",
              "execution_status": null
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "A copy must be created in order to operate on the imported arrays in-place, but will mean duplicating the memory. Do not do this for very large arrays:"
            }
          ]
        },
        {
          "__type": "Blockquote",
          "__tag": 4059,
          "children": [
            {
              "__type": "Code",
              "__tag": 4050,
              "value": ">>> x_np_copy = x_np.copy()\n>>> x_np_copy.sort()  # works",
              "execution_status": null
            }
          ]
        },
        {
          "__type": "Admonition",
          "__tag": 4056,
          "kind": "note",
          "base_type": "note",
          "children": [
            {
              "__type": "AdmonitionTitle",
              "__tag": 4055,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "note "
                }
              ]
            },
            {
              "__type": "Paragraph",
              "__tag": 4045,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "Note that GPU tensors can't be converted to NumPy arrays since NumPy doesn't support GPU devices:"
                }
              ]
            },
            {
              "__type": "Blockquote",
              "__tag": 4059,
              "children": [
                {
                  "__type": "Code",
                  "__tag": 4050,
                  "value": ">>> x_torch = torch.arange(5, device='cuda')\n>>> np.from_dlpack(x_torch)\nTraceback (most recent call last):\n  File \"<stdin>\", line 1, in <module>\nRuntimeError: Unsupported device in DLTensor.",
                  "execution_status": null
                }
              ]
            },
            {
              "__type": "Paragraph",
              "__tag": 4045,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "But, if both libraries support the device the data buffer is on, it is possible to use the "
                },
                {
                  "__type": "InlineCode",
                  "__tag": 4051,
                  "value": "__dlpack__"
                },
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": " protocol (e.g. "
                },
                {
                  "__type": "Link",
                  "__tag": 4049,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "PyTorch"
                    }
                  ],
                  "url": "https://pytorch.org/",
                  "title": ""
                },
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": " and "
                },
                {
                  "__type": "Link",
                  "__tag": 4049,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "CuPy"
                    }
                  ],
                  "url": "https://cupy.dev/",
                  "title": ""
                },
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "):"
                }
              ]
            },
            {
              "__type": "Blockquote",
              "__tag": 4059,
              "children": [
                {
                  "__type": "Code",
                  "__tag": 4050,
                  "value": ">>> x_torch = torch.arange(5, device='cuda')\n>>> x_cupy = cupy.from_dlpack(x_torch)",
                  "execution_status": null
                }
              ]
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Similarly, a NumPy array can be converted to a PyTorch tensor:"
            }
          ]
        },
        {
          "__type": "Blockquote",
          "__tag": 4059,
          "children": [
            {
              "__type": "Code",
              "__tag": 4050,
              "value": ">>> x_np = np.arange(5)\n>>> x_torch = torch.from_dlpack(x_np)",
              "execution_status": null
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Read-only arrays cannot be exported:"
            }
          ]
        },
        {
          "__type": "Blockquote",
          "__tag": 4059,
          "children": [
            {
              "__type": "Code",
              "__tag": 4050,
              "value": ">>> x_np = np.arange(5)\n>>> x_np.flags.writeable = False\n>>> torch.from_dlpack(x_np)  # doctest: +ELLIPSIS\nTraceback (most recent call last):\n  File \"<stdin>\", line 1, in <module>\n  File \".../site-packages/torch/utils/dlpack.py\", line 63, in from_dlpack\n    dlpack = ext_tensor.__dlpack__()\nTypeError: NumPy currently only supports dlpack for writeable arrays",
              "execution_status": null
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Example: DLPack"
        }
      ],
      "level": 2,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "BulletList",
          "__tag": 4053,
          "ordered": false,
          "start": 1,
          "children": [
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "arrays.interface",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "docs",
                        "path": "reference:arrays.interface"
                      },
                      "kind": "exists"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "basics.dispatch",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "docs",
                        "path": "user:basics.dispatch"
                      },
                      "kind": "exists"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "special-attributes-and-methods",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "docs",
                        "path": "reference:arrays.classes"
                      },
                      "kind": "exists"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " (details on the "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "__array_ufunc__"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " and    "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "__array_function__"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " protocols)"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "basics.subclassing",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "docs",
                        "path": "user:basics.subclassing"
                      },
                      "kind": "exists"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " (details on the "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "__array_wrap__"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " and    "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "__array_finalize__"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " methods)"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "specific-array-subtyping",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "docs",
                        "path": "user:c-info.beyond-basics"
                      },
                      "kind": "exists"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " (more details on the implementation of    "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "__array_finalize__"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": ", "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "__array_wrap__"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " and "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "__array_priority__"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": ")"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "NumPy roadmap: interoperability",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "docs",
                        "path": "neps:roadmap"
                      },
                      "kind": "docs"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Link",
                      "__tag": 4049,
                      "children": [
                        {
                          "__type": "Text",
                          "__tag": 4046,
                          "value": "PyTorch documentation on the Bridge with NumPy"
                        }
                      ],
                      "url": "https://pytorch.org/tutorials/beginner/blitz/tensor_tutorial.html#bridge-to-np-label",
                      "title": ""
                    }
                  ]
                }
              ]
            }
          ]
        },
        {
          "__type": "Target",
          "__tag": 4061,
          "label": "CuPy"
        },
        {
          "__type": "Target",
          "__tag": 4061,
          "label": "Sparse"
        },
        {
          "__type": "Target",
          "__tag": 4061,
          "label": "Dask"
        },
        {
          "__type": "Target",
          "__tag": 4061,
          "label": "TensorFlow"
        },
        {
          "__type": "Target",
          "__tag": 4061,
          "label": "PyTorch"
        },
        {
          "__type": "Target",
          "__tag": 4061,
          "label": "XArray"
        },
        {
          "__type": "Target",
          "__tag": 4061,
          "label": "JAX"
        },
        {
          "__type": "Target",
          "__tag": 4061,
          "label": "astropy.units"
        },
        {
          "__type": "Target",
          "__tag": 4061,
          "label": "pint"
        },
        {
          "__type": "Target",
          "__tag": 4061,
          "label": "unyt"
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Further reading"
        }
      ],
      "level": 1,
      "target": null
    }
  ],
  "local_refs": []
}