{
  "__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.dispatch",
  "arbitrary": [
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Numpy's dispatch mechanism, introduced in numpy version v1.16 is the recommended approach for writing custom N-dimensional array containers that are compatible with the numpy API and provide custom implementations of numpy functionality. Applications include "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "dask"
                }
              ],
              "url": "https://docs.dask.org/en/stable/",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " arrays, an N-dimensional array distributed across multiple nodes, and "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "cupy"
                }
              ],
              "url": "https://docs-cupy.chainer.org/en/stable/",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " arrays, an N-dimensional array on a GPU."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "To get a feel for writing custom array containers, we'll begin with a simple example that has rather narrow utility but illustrates the concepts involved."
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": ">>> import numpy as np\n>>> class DiagonalArray:\n...     def __init__(self, N, value):\n...         self._N = N\n...         self._i = value\n...     def __repr__(self):\n...         return f\"{self.__class__.__name__}(N={self._N}, value={self._i})\"\n...     def __array__(self, dtype=None, copy=None):\n...         if copy is False:\n...             raise ValueError(\n...                 \"`copy=False` isn't supported. A copy is always created.\"\n...             )\n...         return self._i * np.eye(self._N, dtype=dtype)",
          "execution_status": null
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Our custom array can be instantiated like:"
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": ">>> arr = DiagonalArray(5, 1)\n>>> arr\nDiagonalArray(N=5, value=1)",
          "execution_status": null
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "We can convert to a numpy array using "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "numpy.array",
              "reference": {
                "__type": "RefInfo",
                "__tag": 4000,
                "module": "numpy",
                "version": "*",
                "kind": "api",
                "path": "numpy:array"
              },
              "kind": "module"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " or "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "numpy.asarray",
              "reference": {
                "__type": "RefInfo",
                "__tag": 4000,
                "module": "numpy",
                "version": "*",
                "kind": "api",
                "path": "numpy:asarray"
              },
              "kind": "module"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", which will call its "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " method to obtain a standard "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "numpy.ndarray"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "."
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": ">>> np.asarray(arr)\narray([[1., 0., 0., 0., 0.],\n       [0., 1., 0., 0., 0.],\n       [0., 0., 1., 0., 0.],\n       [0., 0., 0., 1., 0.],\n       [0., 0., 0., 0., 1.]])",
          "execution_status": null
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "The "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " method can optionally accept a "
            },
            {
              "__type": "InlineRole",
              "__tag": 4003,
              "value": "dtype",
              "domain": null,
              "role": null,
              "inventory": null
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " argument. If provided, this argument specifies the desired data type for the resulting NumPy array. Your implementation should attempt to convert the data to this "
            },
            {
              "__type": "InlineRole",
              "__tag": 4003,
              "value": "dtype",
              "domain": null,
              "role": null,
              "inventory": null
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " if possible. If the conversion is not supported, it's generally best to fall back to a default type or raise a "
            },
            {
              "__type": "InlineRole",
              "__tag": 4003,
              "value": "TypeError",
              "domain": null,
              "role": null,
              "inventory": null
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " or "
            },
            {
              "__type": "InlineRole",
              "__tag": 4003,
              "value": "ValueError",
              "domain": null,
              "role": null,
              "inventory": null
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Here's an example demonstrating its use with "
            },
            {
              "__type": "InlineRole",
              "__tag": 4003,
              "value": "dtype",
              "domain": null,
              "role": null,
              "inventory": null
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " specification:"
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": ">>> np.asarray(arr, dtype=np.float32)\narray([[1., 0., 0., 0., 0.],\n       [0., 1., 0., 0., 0.],\n       [0., 0., 1., 0., 0.],\n       [0., 0., 0., 1., 0.],\n       [0., 0., 0., 0., 1.]], dtype=float32)",
          "execution_status": null
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "If we operate on "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "arr"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " with a numpy function, numpy will again use the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " interface to convert it to an array and then apply the function in the usual way."
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": ">>> np.multiply(arr, 2)\narray([[2., 0., 0., 0., 0.],\n       [0., 2., 0., 0., 0.],\n       [0., 0., 2., 0., 0.],\n       [0., 0., 0., 2., 0.],\n       [0., 0., 0., 0., 2.]])",
          "execution_status": null
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Notice that the return type is a standard "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "numpy.ndarray"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "."
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": ">>> type(np.multiply(arr, 2))\n<class 'numpy.ndarray'>",
          "execution_status": null
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "How can we pass our custom array type through this function? Numpy allows a class to indicate that it would like to handle computations in a custom-defined way through the interfaces "
            },
            {
              "__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": ". Let's take one at a time, starting with "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array_ufunc__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ". This method covers "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "ufuncs",
              "reference": {
                "__type": "LocalRef",
                "__tag": 4022,
                "kind": "docs",
                "path": "reference:ufuncs"
              },
              "kind": "exists"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", a class of functions that includes, for example, "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "numpy.multiply",
              "reference": {
                "__type": "RefInfo",
                "__tag": 4000,
                "module": "numpy",
                "version": "*",
                "kind": "api",
                "path": "numpy:multiply"
              },
              "kind": "module"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "numpy.sin",
              "reference": {
                "__type": "RefInfo",
                "__tag": 4000,
                "module": "numpy",
                "version": "*",
                "kind": "api",
                "path": "numpy:sin"
              },
              "kind": "module"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "The "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array_ufunc__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " receives:"
            }
          ]
        },
        {
          "__type": "BulletList",
          "__tag": 4053,
          "ordered": false,
          "start": 1,
          "children": [
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "ufunc"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": ", a function like "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "numpy.multiply"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "method"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": ", a string, differentiating between "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "numpy.multiply(...)"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " and   variants like "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "numpy.multiply.outer"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": ", "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "numpy.multiply.accumulate"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": ", and so   on.  For the common case, "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "numpy.multiply(...)"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": ", "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "method == '__call__'"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "."
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "inputs"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": ", which could be a mixture of different types"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "kwargs"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": ", keyword arguments passed to the function"
                    }
                  ]
                }
              ]
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "For this example we will only handle the method "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__call__"
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": ">>> from numbers import Number\n>>> class DiagonalArray:\n...     def __init__(self, N, value):\n...         self._N = N\n...         self._i = value\n...     def __repr__(self):\n...         return f\"{self.__class__.__name__}(N={self._N}, value={self._i})\"\n...     def __array__(self, dtype=None, copy=None):\n...         if copy is False:\n...             raise ValueError(\n...                 \"`copy=False` isn't supported. A copy is always created.\"\n...             )\n...         return self._i * np.eye(self._N, dtype=dtype)\n...     def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):\n...         if method == '__call__':\n...             N = None\n...             scalars = []\n...             for input in inputs:\n...                 if isinstance(input, Number):\n...                     scalars.append(input)\n...                 elif isinstance(input, self.__class__):\n...                     scalars.append(input._i)\n...                     if N is not None:\n...                         if N != input._N:\n...                             raise TypeError(\"inconsistent sizes\")\n...                     else:\n...                         N = input._N\n...                 else:\n...                     return NotImplemented\n...             return self.__class__(N, ufunc(*scalars, **kwargs))\n...         else:\n...             return NotImplemented",
          "execution_status": null
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Now our custom array type passes through numpy functions."
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": ">>> arr = DiagonalArray(5, 1)\n>>> np.multiply(arr, 3)\nDiagonalArray(N=5, value=3)\n>>> np.add(arr, 3)\nDiagonalArray(N=5, value=4)\n>>> np.sin(arr)\nDiagonalArray(N=5, value=0.8414709848078965)",
          "execution_status": null
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "At this point "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "arr + 3"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " does not work."
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": ">>> arr + 3\nTraceback (most recent call last):\n...\nTypeError: unsupported operand type(s) for +: 'DiagonalArray' and 'int'",
          "execution_status": null
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "To support it, we need to define the Python interfaces "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__add__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__lt__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", and so on to dispatch to the corresponding ufunc. We can achieve this conveniently by inheriting from the mixin "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "NDArrayOperatorsMixin",
              "reference": {
                "__type": "RefInfo",
                "__tag": 4000,
                "module": "numpy",
                "version": "*",
                "kind": "api",
                "path": "numpy.lib.mixins:NDArrayOperatorsMixin"
              },
              "kind": "module"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "."
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": ">>> import numpy.lib.mixins\n>>> class DiagonalArray(numpy.lib.mixins.NDArrayOperatorsMixin):\n...     def __init__(self, N, value):\n...         self._N = N\n...         self._i = value\n...     def __repr__(self):\n...         return f\"{self.__class__.__name__}(N={self._N}, value={self._i})\"\n...     def __array__(self, dtype=None, copy=None):\n...         if copy is False:\n...             raise ValueError(\n...                 \"`copy=False` isn't supported. A copy is always created.\"\n...             )\n...         return self._i * np.eye(self._N, dtype=dtype)\n...     def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):\n...         if method == '__call__':\n...             N = None\n...             scalars = []\n...             for input in inputs:\n...                 if isinstance(input, Number):\n...                     scalars.append(input)\n...                 elif isinstance(input, self.__class__):\n...                     scalars.append(input._i)\n...                     if N is not None:\n...                         if N != input._N:\n...                             raise TypeError(\"inconsistent sizes\")\n...                     else:\n...                         N = input._N\n...                 else:\n...                     return NotImplemented\n...             return self.__class__(N, ufunc(*scalars, **kwargs))\n...         else:\n...             return NotImplemented",
          "execution_status": null
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": ">>> arr = DiagonalArray(5, 1)\n>>> arr + 3\nDiagonalArray(N=5, value=4)\n>>> arr > 0\nDiagonalArray(N=5, value=True)",
          "execution_status": null
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Now let's tackle "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array_function__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ". We'll create dict that maps numpy functions to our custom variants."
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": ">>> HANDLED_FUNCTIONS = {}\n>>> class DiagonalArray(numpy.lib.mixins.NDArrayOperatorsMixin):\n...     def __init__(self, N, value):\n...         self._N = N\n...         self._i = value\n...     def __repr__(self):\n...         return f\"{self.__class__.__name__}(N={self._N}, value={self._i})\"\n...     def __array__(self, dtype=None, copy=None):\n...         if copy is False:\n...             raise ValueError(\n...                 \"`copy=False` isn't supported. A copy is always created.\"\n...             )\n...         return self._i * np.eye(self._N, dtype=dtype)\n...     def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):\n...         if method == '__call__':\n...             N = None\n...             scalars = []\n...             for input in inputs:\n...                 # In this case we accept only scalar numbers or DiagonalArrays.\n...                 if isinstance(input, Number):\n...                     scalars.append(input)\n...                 elif isinstance(input, self.__class__):\n...                     scalars.append(input._i)\n...                     if N is not None:\n...                         if N != input._N:\n...                             raise TypeError(\"inconsistent sizes\")\n...                     else:\n...                         N = input._N\n...                 else:\n...                     return NotImplemented\n...             return self.__class__(N, ufunc(*scalars, **kwargs))\n...         else:\n...             return NotImplemented\n...     def __array_function__(self, func, types, args, kwargs):\n...         if func not in HANDLED_FUNCTIONS:\n...             return NotImplemented\n...         # Note: this allows subclasses that don't override\n...         # __array_function__ to handle DiagonalArray objects.\n...         if not all(issubclass(t, self.__class__) for t in types):\n...             return NotImplemented\n...         return HANDLED_FUNCTIONS[func](*args, **kwargs)\n...",
          "execution_status": null
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "A convenient pattern is to define a decorator "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "implements"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " that can be used to add functions to "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "HANDLED_FUNCTIONS"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "."
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": ">>> def implements(np_function):\n...    \"Register an __array_function__ implementation for DiagonalArray objects.\"\n...    def decorator(func):\n...        HANDLED_FUNCTIONS[np_function] = func\n...        return func\n...    return decorator\n...",
          "execution_status": null
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Now we write implementations of numpy functions for "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "DiagonalArray"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ". For completeness, to support the usage "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "arr.sum()"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " add a method "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "sum"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " that calls "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "numpy.sum(self)"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", and the same for "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "mean"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "."
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": ">>> @implements(np.sum)\n... def sum(arr):\n...     \"Implementation of np.sum for DiagonalArray objects\"\n...     return arr._i * arr._N\n...\n>>> @implements(np.mean)\n... def mean(arr):\n...     \"Implementation of np.mean for DiagonalArray objects\"\n...     return arr._i / arr._N\n...\n>>> arr = DiagonalArray(5, 1)\n>>> np.sum(arr)\n5\n>>> np.mean(arr)\n0.2",
          "execution_status": null
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "If the user tries to use any numpy functions not included in "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "HANDLED_FUNCTIONS"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", a "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "TypeError"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " will be raised by numpy, indicating that this operation is not supported. For example, concatenating two "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "DiagonalArrays"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " does not produce another diagonal array, so it is not supported."
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": ">>> np.concatenate([arr, arr])\nTraceback (most recent call last):\n...\nTypeError: no implementation found for 'numpy.concatenate' on types that implement __array_function__: [<class '__main__.DiagonalArray'>]",
          "execution_status": null
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Additionally, our implementations of "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "sum"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "mean"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " do not accept the optional arguments that numpy's implementation does."
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": ">>> np.sum(arr, axis=0)\nTraceback (most recent call last):\n...\nTypeError: sum() got an unexpected keyword argument 'axis'",
          "execution_status": null
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "The user always has the option of converting to a normal "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "numpy.ndarray"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " with "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "numpy.asarray",
              "reference": {
                "__type": "RefInfo",
                "__tag": 4000,
                "module": "numpy",
                "version": "*",
                "kind": "api",
                "path": "numpy:asarray"
              },
              "kind": "module"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and using standard numpy from there."
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": ">>> np.concatenate([np.asarray(arr), np.asarray(arr)])\narray([[1., 0., 0., 0., 0.],\n       [0., 1., 0., 0., 0.],\n       [0., 0., 1., 0., 0.],\n       [0., 0., 0., 1., 0.],\n       [0., 0., 0., 0., 1.],\n       [1., 0., 0., 0., 0.],\n       [0., 1., 0., 0., 0.],\n       [0., 0., 1., 0., 0.],\n       [0., 0., 0., 1., 0.],\n       [0., 0., 0., 0., 1.]])",
          "execution_status": null
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "The implementation of "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "DiagonalArray"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " in this example only handles the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "np.sum"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "np.mean"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " functions for brevity. Many other functions in the Numpy API are also available to wrap and a full-fledged custom array container can explicitly support all functions that Numpy makes available to wrap."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Numpy provides some utilities to aid testing of custom array containers that implement 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 in the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "numpy.testing.overrides"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " namespace."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "To check if a Numpy function can be overridden via "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array_ufunc__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", you can use "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "allows_array_ufunc_override",
              "reference": {
                "__type": "RefInfo",
                "__tag": 4000,
                "module": "numpy",
                "version": "*",
                "kind": "api",
                "path": "numpy.testing.overrides:allows_array_ufunc_override"
              },
              "kind": "module"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ":"
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": ">>> from numpy.testing.overrides import allows_array_ufunc_override\n>>> allows_array_ufunc_override(np.add)\nTrue",
          "execution_status": null
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Similarly, you can check if a function can be overridden via "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array_function__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " using "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "allows_array_function_override",
              "reference": {
                "__type": "RefInfo",
                "__tag": 4000,
                "module": "numpy",
                "version": "*",
                "kind": "api",
                "path": "numpy.testing.overrides:allows_array_function_override"
              },
              "kind": "module"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Lists of every overridable function in the Numpy API are also available via "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "get_overridable_numpy_array_functions",
              "reference": {
                "__type": "RefInfo",
                "__tag": 4000,
                "module": "numpy",
                "version": "*",
                "kind": "api",
                "path": "numpy.testing.overrides:get_overridable_numpy_array_functions"
              },
              "kind": "module"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " for functions that support the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array_function__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " protocol and "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "get_overridable_numpy_ufuncs",
              "reference": {
                "__type": "RefInfo",
                "__tag": 4000,
                "module": "numpy",
                "version": "*",
                "kind": "api",
                "path": "numpy.testing.overrides:get_overridable_numpy_ufuncs"
              },
              "kind": "module"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " for functions that support the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__array_ufunc__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " protocol. Both functions return sets of functions that are present in the Numpy public API. User-defined ufuncs or ufuncs defined in other libraries that depend on Numpy are not present in these sets."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Refer to the "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "dask source code"
                }
              ],
              "url": "https://github.com/dask/dask",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "cupy source code"
                }
              ],
              "url": "https://github.com/cupy/cupy",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "  for more fully-worked examples of custom array containers."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "See also "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "NEP 18<neps:nep-0018-array-function-protocol>",
              "reference": {
                "__type": "LocalRef",
                "__tag": 4022,
                "kind": "docs",
                "path": "NEP 18<neps:nep-0018-array-function-protocol>"
              },
              "kind": "docs"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Writing custom array containers"
        }
      ],
      "level": 0,
      "target": "basics.dispatch"
    }
  ],
  "local_refs": []
}