<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40"><head><meta http-equiv=Content-Type content="text/html; charset=utf-8"><meta name=Generator content="Microsoft Word 15 (filtered medium)"><style><!--
/* Font Definitions */
@font-face
        {font-family:Courier;
        panose-1:0 0 0 0 0 0 0 0 0 0;}
@font-face
        {font-family:"Cambria Math";
        panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
        {font-family:Calibri;
        panose-1:2 15 5 2 2 2 4 3 2 4;}
@font-face
        {font-family:Consolas;
        panose-1:2 11 6 9 2 2 4 3 2 4;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
        {margin:0cm;
        margin-bottom:.0001pt;
        font-size:11.0pt;
        font-family:"Calibri",sans-serif;}
a:link, span.MsoHyperlink
        {mso-style-priority:99;
        color:blue;
        text-decoration:underline;}
a:visited, span.MsoHyperlinkFollowed
        {mso-style-priority:99;
        color:purple;
        text-decoration:underline;}
pre
        {mso-style-priority:99;
        mso-style-link:"HTML Preformatted Char";
        margin:0cm;
        margin-bottom:.0001pt;
        font-size:10.0pt;
        font-family:"Courier New";}
p.msonormal0, li.msonormal0, div.msonormal0
        {mso-style-name:msonormal;
        mso-margin-top-alt:auto;
        margin-right:0cm;
        mso-margin-bottom-alt:auto;
        margin-left:0cm;
        font-size:11.0pt;
        font-family:"Calibri",sans-serif;}
span.HTMLPreformattedChar
        {mso-style-name:"HTML Preformatted Char";
        mso-style-priority:99;
        mso-style-link:"HTML Preformatted";
        font-family:"Consolas",serif;}
span.EmailStyle21
        {mso-style-type:personal-reply;
        font-family:"Calibri",sans-serif;
        color:windowtext;}
.MsoChpDefault
        {mso-style-type:export-only;
        font-size:10.0pt;}
@page WordSection1
        {size:612.0pt 792.0pt;
        margin:70.85pt 70.85pt 2.0cm 70.85pt;}
div.WordSection1
        {page:WordSection1;}
--></style></head><body lang=DE link=blue vlink=purple><div class=WordSection1><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'>Thanks to all the feedback, we have a new PR of NEP-31.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'>Please find the full-text quoted below:<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>============================================================<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>NEP 31 — Context-local and global overrides of the NumPy API<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>============================================================<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>:Author: Hameer Abbasi <habbasi@quansight.com><o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>:Author: Ralf Gommers <rgommers@quansight.com><o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>:Author: Peter Bell <pbell@quansight.com><o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>:Status: Draft<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>:Type: Standards Track<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>:Created: 2019-08-22<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>Abstract<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>--------<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>This NEP proposes to make all of NumPy's public API overridable via an<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>extensible backend mechanism.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>Acceptance of this NEP means NumPy would provide global and context-local<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>overrides, as well as a dispatch mechanism similar to NEP-18 [2]_. First<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>experiences with ``__array_function__`` show that it is necessary to be able<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>to override NumPy functions that *do not take an array-like argument*, and<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>hence aren't overridable via ``__array_function__``. The most pressing need is<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>array creation and coercion functions, such as ``numpy.zeros`` or<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>``numpy.asarray``; see e.g. NEP-30 [9]_.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>This NEP proposes to allow, in an opt-in fashion, overriding any part of the<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>NumPy API. It is intended as a comprehensive resolution to NEP-22 [3]_, and<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>obviates the need to add an ever-growing list of new protocols for each new<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>type of function or object that needs to become overridable.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>Motivation and Scope<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>--------------------<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>The motivation behind ``uarray`` is manyfold: First, there have been several<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>attempts to allow dispatch of parts of the NumPy API, including (most<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>prominently), the ``__array_ufunc__`` protocol in NEP-13 [4]_, and the<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>``__array_function__`` protocol in NEP-18 [2]_, but this has shown the need<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>for further protocols to be developed, including a protocol for coercion (see<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>[5]_, [9]_). The reasons these overrides are needed have been extensively<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>discussed in the references, and this NEP will not attempt to go into the<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>details of why these are needed; but in short: It is necessary for library<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>authors to be able to coerce arbitrary objects into arrays of their own types,<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>such as CuPy needing to coerce to a CuPy array, for example, instead of<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>a NumPy array.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>These kinds of overrides are useful for both the end-user as well as library<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>authors. End-users may have written or wish to write code that they then later<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>speed up or move to a different implementation, say PyData/Sparse. They can do<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>this simply by setting a backend. Library authors may also wish to write code<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>that is portable across array implementations, for example ``sklearn`` may wish<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>to write code for a machine learning algorithm that is portable across array<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>implementations while also using array creation functions.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>This NEP takes a holistic approach: It assumes that there are parts of<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>the API that need to be overridable, and that these will grow over time. It<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>provides a general framework and a mechanism to avoid a design of a new<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>protocol each time this is required. This was the goal of ``uarray``: to<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>allow for overrides in an API without needing the design of a new protocol.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>This NEP proposes the following: That ``unumpy`` [8]_  becomes the<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>recommended override mechanism for the parts of the NumPy API not yet covered<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>by ``__array_function__`` or ``__array_ufunc__``, and that ``uarray`` is<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>vendored into a new namespace within NumPy to give users and downstream<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>dependencies access to these overrides.  This vendoring mechanism is similar<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>to what SciPy decided to do for making ``scipy.fft`` overridable (see [10]_).<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>Detailed description<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>--------------------<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>Using overrides<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>~~~~~~~~~~~~~~~<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>The way we propose the overrides will be used by end users is::<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>    # On the library side<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>    import numpy.overridable as unp<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>    def library_function(array):<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>        array = unp.asarray(array)<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>        # Code using unumpy as usual<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>        return array<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>    # On the user side:<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>    import numpy.overridable as unp<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>    import uarray as ua<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>    import dask.array as da<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>    ua.register_backend(da)<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>    library_function(dask_array)  # works and returns dask_array<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>    with unp.set_backend(da):<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>        library_function([1, 2, 3, 4])  # actually returns a Dask array.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>Here, ``backend`` can be any compatible object defined either by NumPy or an<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>external library, such as Dask or CuPy. Ideally, it should be the module<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>``dask.array`` or ``cupy`` itself.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>Composing backends<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>~~~~~~~~~~~~~~~~~~<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>There are some backends which may depend on other backends, for example xarray<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>depending on `numpy.fft`, and transforming a time axis into a frequency axis,<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>or Dask/xarray holding an array other than a NumPy array inside it. This would<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>be handled in the following manner inside code::<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>    with ua.set_backend(cupy), ua.set_backend(dask.array):<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>        # Code that has distributed GPU arrays here<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>Proposals<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>~~~~~~~~~<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>The only change this NEP proposes at its acceptance, is to make ``unumpy`` the<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>officially recommended way to override NumPy, along with making some submodules<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>overridable by default via ``uarray``. ``unumpy`` will remain a separate<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>repository/package (which we propose to vendor to avoid a hard dependency, and<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>use the separate ``unumpy`` package only if it is installed, rather than depend<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>on for the time being). In concrete terms, ``numpy.overridable`` becomes an<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>alias for ``unumpy``, if available with a fallback to the a vendored version if<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>not. ``uarray`` and ``unumpy`` and will be developed primarily with the input<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>of duck-array authors and secondarily, custom dtype authors, via the usual<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>GitHub workflow. There are a few reasons for this:<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* Faster iteration in the case of bugs or issues.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* Faster design changes, in the case of needed functionality.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* ``unumpy`` will work with older versions of NumPy as well.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* The user and library author opt-in to the override process,<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>  rather than breakages happening when it is least expected.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>  In simple terms, bugs in ``unumpy`` mean that ``numpy`` remains<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>  unaffected.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* For ``numpy.fft``, ``numpy.linalg`` and ``numpy.random``, the functions in<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>  the main namespace will mirror those in the ``numpy.overridable`` namespace.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>  The reason for this is that there may exist functions in the in these<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>  submodules that need backends, even for ``numpy.ndarray`` inputs.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>Advantanges of ``unumpy`` over other solutions<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>``unumpy`` offers a number of advantanges over the approach of defining a new<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>protocol for every problem encountered: Whenever there is something requiring<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>an override, ``unumpy`` will be able to offer a unified API with very minor<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>changes. For example:<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* ``ufunc`` objects can be overridden via their ``__call__``, ``reduce`` and<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>  other methods.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* Other functions can be overridden in a similar fashion.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* ``np.asduckarray`` goes away, and becomes ``np.overridable.asarray`` with a<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>  backend set.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* The same holds for array creation functions such as ``np.zeros``,<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>  ``np.empty`` and so on.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>This also holds for the future: Making something overridable would require only<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>minor changes to ``unumpy``.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>Another promise ``unumpy`` holds is one of default implementations. Default<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>implementations can be provided for any multimethod, in terms of others. This<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>allows one to override a large part of the NumPy API by defining only a small<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>part of it. This is to ease the creation of new duck-arrays, by providing<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>default implementations of many functions that can be easily expressed in<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>terms of others, as well as a repository of utility functions that help in the<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>implementation of duck-arrays that most duck-arrays would require. This would<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>allow us to avoid designing entire protocols, e.g., a protocol for stacking<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>and concatenating would be replaced by simply implementing ``stack`` and/or<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>``concatenate`` and then providing default implementations for everything else<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>in that class. The same applies for transposing, and many other functions for<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>which protocols haven't been proposed, such as ``isin`` in terms of ``in1d``,<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>``setdiff1d`` in terms of ``unique``, and so on.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>It also allows one to override functions in a manner which<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>``__array_function__`` simply cannot, such as overriding ``np.einsum`` with the<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>version from the ``opt_einsum`` package, or Intel MKL overriding FFT, BLAS<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>or ``ufunc`` objects. They would define a backend with the appropriate<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>multimethods, and the user would select them via a ``with`` statement, or<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>registering them as a backend.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>The last benefit is a clear way to coerce to a given backend (via the<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>``coerce`` keyword in ``ua.set_backend``), and a protocol<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>for coercing not only arrays, but also ``dtype`` objects and ``ufunc`` objects<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>with similar ones from other libraries. This is due to the existence of actual,<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>third party dtype packages, and their desire to blend into the NumPy ecosystem<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>(see [6]_). This is a separate issue compared to the C-level dtype redesign<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>proposed in [7]_, it's about allowing third-party dtype implementations to<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>work with NumPy, much like third-party array implementations. These can provide<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>features such as, for example, units, jagged arrays or other such features that<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>are outside the scope of NumPy.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>Mixing NumPy and ``unumpy`` in the same file<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>Normally, one would only want to import only one of ``unumpy`` or ``numpy``,<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>you would import it as ``np`` for familiarity. However, there may be situations<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>where one wishes to mix NumPy and the overrides, and there are a few ways to do<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>this, depending on the user's style::<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>    from numpy import overridable as unp<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>    import numpy as np<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>or::<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>    import numpy as np<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>    # Use unumpy via np.overridable<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>Duck-array coercion<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>~~~~~~~~~~~~~~~~~~~<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>There are inherent problems about returning objects that are not NumPy arrays<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>from ``numpy.array`` or ``numpy.asarray``, particularly in the context of C/C++<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>or Cython code that may get an object with a different memory layout than the<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>one it expects. However, we believe this problem may apply not only to these<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>two functions but all functions that return NumPy arrays. For this reason,<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>overrides are opt-in for the user, by using the submodule ``numpy.overridable``<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>rather than ``numpy``. NumPy will continue to work unaffected by anything in<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>``numpy.overridable``.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>If the user wishes to obtain a NumPy array, there are two ways of doing it:<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>1. Use ``numpy.asarray`` (the non-overridable version).<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>2. Use ``numpy.overridable.asarray`` with the NumPy backend set and coercion<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>   enabled<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>Aliases outside of the ``numpy.overridable`` namespace<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>All functionality in ``numpy.random``, ``numpy.linalg`` and ``numpy.fft``<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>will be aliased to their respective overridable versions inside<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>``numpy.overridable``. The reason for this is that there are alternative<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>implementations of RNGs (``mkl-random``), linear algebra routines (``eigen``,<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>``blis``) and FFT routines (``mkl-fft``, ``pyFFTW``) that need to operate on<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>``numpy.ndarray`` inputs, but still need the ability to switch behaviour.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>This is different from monkeypatching in a few different ways:<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* The caller-facing signature of the function is always the same,<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>  so there is at least the loose sense of an API contract. Monkeypatching<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>  does not provide this ability.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* There is the ability of locally switching the backend.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* It has been `suggested <http://numpy-discussion.10968.n7.nabble.com/NEP-31-Context-local-and-global-overrides-of-the-NumPy-API-tp47452p47472.html>`_<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>  that the reason that 1.17 hasn't landed in the Anaconda defaults channel is<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>  due to the incompatibility between monkeypatching and ``__array_function__``,<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>  as monkeypatching would bypass the protocol completely.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* Statements of the form ``from numpy import x; x`` and ``np.x`` would have<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>  different results depending on whether the import was made before or<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>  after monkeypatching happened.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>All this isn't possible at all with ``__array_function__`` or<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>``__array_ufunc__``.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>It has been formally realised (at least in part) that a backend system is<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>needed for this, in the `NumPy roadmap <https://numpy.org/neps/roadmap.html#other-functionality>`_.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>For ``numpy.random``, it's still necessary to make the C-API fit the one<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>proposed in `NEP-19 <https://numpy.org/neps/nep-0019-rng-policy.html>`_.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>This is impossible for `mkl-random`, because then it would need to be<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>rewritten to fit that framework. The guarantees on stream<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>compatibility will be the same as before, but if there's a backend that affects<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>``numpy.random`` set, we make no guarantees about stream compatibility, and it<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>is up to the backend author to provide their own guarantees.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>Providing a way for implicit dispatch<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>It has been suggested that the ability to dispatch methods which do not take<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>a dispatchable is needed, while guessing that backend from another dispatchable.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>As a concrete example, consider the following:<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>.. code:: python<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>    with unumpy.determine_backend(array_like, np.ndarray):<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>        unumpy.arange(len(array_like))<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>While this does not exist yet in ``uarray``, it is trivial to add it. The need for<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>this kind of code exists because one might want to have an alternative for the<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>proposed ``*_like`` functions, or the ``like=`` keyword argument. The need for these<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>exists because there are functions in the NumPy API that do not take a dispatchable<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>argument, but there is still the need to select a backend based on a different<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>dispatchable.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>The need for an opt-in module<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>The need for an opt-in module is realised because of a few reasons:<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* There are parts of the API (like `numpy.asarray`) that simply cannot be<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>  overridden due to incompatibility concerns with C/Cython extensions, however,<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>  one may want to coerce to a duck-array using ``asarray`` with a backend set.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* There are possible issues around an implicit option and monkeypatching, such<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>  as those mentioned above.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>NEP 18 notes that this may require maintenance of two separate APIs. However,<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>this burden may be lessened by, for example, parametrizing all tests over<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>``numpy.overridable`` separately via a fixture. This also has the side-effect<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>of thoroughly testing it, unlike ``__array_function__``. We also feel that it<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>provides an oppurtunity to separate the NumPy API contract properly from the<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>implementation.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>Benefits to end-users and mixing backends<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>Mixing backends is easy in ``uarray``, one only has to do:<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>.. code:: python<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>    # Explicitly say which backends you want to mix<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>    ua.register_backend(backend1)<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>    ua.register_backend(backend2)<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>    ua.register_backend(backend3)<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>    # Freely use code that mixes backends here.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>The benefits to end-users extend beyond just writing new code. Old code<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>(usually in the form of scripts) can be easily ported to different backends<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>by a simple import switch and a line adding the preferred backend. This way,<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>users may find it easier to port existing code to GPU or distributed computing.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>Related Work<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>------------<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>Other override mechanisms<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>~~~~~~~~~~~~~~~~~~~~~~~~~<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* NEP-18, the ``__array_function__`` protocol. [2]_<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* NEP-13, the ``__array_ufunc__`` protocol. [3]_<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* NEP-30, the ``__duck_array__`` protocol. [9]_<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>Existing NumPy-like array implementations<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* Dask: https://dask.org/<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* CuPy: https://cupy.chainer.org/<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* PyData/Sparse: https://sparse.pydata.org/<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* Xnd: https://xnd.readthedocs.io/<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* Astropy's Quantity: https://docs.astropy.org/en/stable/units/<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>Existing and potential consumers of alternative arrays<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* Dask: https://dask.org/<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* scikit-learn: https://scikit-learn.org/<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* xarray: https://xarray.pydata.org/<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* TensorLy: http://tensorly.org/<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>Existing alternate dtype implementations<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* ``ndtypes``: https://ndtypes.readthedocs.io/en/latest/<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* Datashape: https://datashape.readthedocs.io<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* Plum: https://plum-py.readthedocs.io/<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>Alternate implementations of parts of the NumPy API<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* ``mkl_random``: https://github.com/IntelPython/mkl_random<o:p></o:p></span></p><p class=MsoNormal><span style='font-family:Courier;mso-fareast-language:EN-US'>* ``mkl_fft``: https://github.com/IntelPython/mkl_fft<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* ``bottleneck``: https://github.com/pydata/bottleneck<o:p></o:p></span></p><p class=MsoNormal><span style='font-family:Courier;mso-fareast-language:EN-US'>* ``opt_einsum``: https://github.com/dgasmith/opt_einsum<o:p></o:p></span></p><p class=MsoNormal><span style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>Implementation<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>--------------<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>The implementation of this NEP will require the following steps:<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* Implementation of ``uarray`` multimethods corresponding to the<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>  NumPy API, including classes for overriding ``dtype``, ``ufunc``<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>  and ``array`` objects, in the ``unumpy`` repository.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* Moving backends from ``unumpy`` into the respective array libraries.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>``uarray`` Primer<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>~~~~~~~~~~~~~~~~~<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>**Note:** *This section will not attempt to go into too much detail about<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>uarray, that is the purpose of the uarray documentation.* [1]_<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>*However, the NumPy community will have input into the design of<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>uarray, via the issue tracker.*<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>``unumpy`` is the interface that defines a set of overridable functions<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>(multimethods) compatible with the numpy API. To do this, it uses the<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>``uarray`` library. ``uarray`` is a general purpose tool for creating<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>multimethods that dispatch to one of multiple different possible backend<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>implementations. In this sense, it is similar to the ``__array_function__``<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>protocol but with the key difference that the backend is explicitly installed<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>by the end-user and not coupled into the array type.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>Decoupling the backend from the array type gives much more flexibility to<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>end-users and backend authors. For example, it is possible to:<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* override functions not taking arrays as arguments<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* create backends out of source from the array type<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* install multiple backends for the same array type<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>This decoupling also means that ``uarray`` is not constrained to dispatching<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>over array-like types. The backend is free to inspect the entire set of<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>function arguments to determine if it can implement the function e.g. ``dtype``<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>parameter dispatching.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>Defining backends<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>^^^^^^^^^^^^^^^^^<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>``uarray`` consists of two main protocols: ``__ua_convert__`` and<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>``__ua_function__``, called in that order, along with ``__ua_domain__``.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>``__ua_convert__`` is for conversion and coercion. It has the signature<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>``(dispatchables, coerce)``, where ``dispatchables`` is an iterable of<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>``ua.Dispatchable`` objects and ``coerce`` is a boolean indicating whether or<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>not to force the conversion. ``ua.Dispatchable`` is a simple class consisting<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>of three simple values: ``type``, ``value``, and ``coercible``.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>``__ua_convert__`` returns an iterable of the converted values, or<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>``NotImplemented`` in the case of failure.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>``__ua_function__`` has the signature ``(func, args, kwargs)`` and defines<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>the actual implementation of the function. It recieves the function and its<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>arguments. Returning ``NotImplemented`` will cause a move to the default<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>implementation of the function if one exists, and failing that, the next<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>backend.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>Here is what will happen assuming a ``uarray`` multimethod is called:<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>1. We canonicalise the arguments so any arguments without a default<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>   are placed in ``*args`` and those with one are placed in ``**kwargs``.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>2. We check the list of backends.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>   a. If it is empty, we try the default implementation.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>3. We check if the backend's ``__ua_convert__`` method exists. If it exists:<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>   a. We pass it the output of the dispatcher,<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>      which is an iterable of ``ua.Dispatchable`` objects.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>   b. We feed this output, along with the arguments,<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>      to the argument replacer. ``NotImplemented`` means we move to 3<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>      with the next backend.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>   c. We store the replaced arguments as the new arguments.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>4. We feed the arguments into ``__ua_function__``, and return the output, and<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>   exit if it isn't ``NotImplemented``.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>5. If the default implementation exists, we try it with the current backend.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>6. On failure,  we move to 3 with the next backend. If there are no more<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>   backends, we move to 7.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>7. We raise a ``ua.BackendNotImplementedError``.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>Defining overridable multimethods<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>To define an overridable function (a multimethod), one needs a few things:<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>1. A dispatcher that returns an iterable of ``ua.Dispatchable`` objects.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>2. A reverse dispatcher that replaces dispatchable values with the supplied<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>   ones.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>3. A domain.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>4. Optionally, a default implementation, which can be provided in terms of<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>   other multimethods.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>As an example, consider the following::<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>    import uarray as ua<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>    def full_argreplacer(args, kwargs, dispatchables):<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>        def full(shape, fill_value, dtype=None, order='C'):<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>            return (shape, fill_value), dict(<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>                dtype=dispatchables[0],<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>                order=order<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>            )<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>        return full(*args, **kwargs)<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>    @ua.create_multimethod(full_argreplacer, domain="numpy")<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>    def full(shape, fill_value, dtype=None, order='C'):<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>        return (ua.Dispatchable(dtype, np.dtype),)<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>A large set of examples can be found in the ``unumpy`` repository, [8]_.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>This simple act of overriding callables allows us to override:<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* Methods<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* Properties, via ``fget`` and ``fset``<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* Entire objects, via ``__get__``.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>Examples for NumPy<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>^^^^^^^^^^^^^^^^^^<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>A library that implements a NumPy-like API will use it in the following<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>manner (as an example)::<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>    import numpy.overridable as unp<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>    _ua_implementations = {}<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>    __ua_domain__ = "numpy"<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>    def __ua_function__(func, args, kwargs):<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>        fn = _ua_implementations.get(func, None)<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>        return fn(*args, **kwargs) if fn is not None else NotImplemented<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>    def implements(ua_func):<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>        def inner(func):<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>            _ua_implementations[ua_func] = func<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>            return func<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>        return inner<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>    @implements(unp.asarray)<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>    def asarray(a, dtype=None, order=None):<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>        # Code here<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>        # Either this method or __ua_convert__ must<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>        # return NotImplemented for unsupported types,<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>        # Or they shouldn't be marked as dispatchable.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>    # Provides a default implementation for ones and zeros.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>    @implements(unp.full)<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>    def full(shape, fill_value, dtype=None, order='C'):<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>        # Code here<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>Backward compatibility<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>----------------------<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>There are no backward incompatible changes proposed in this NEP.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>Alternatives<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>------------<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>The current alternative to this problem is a combination of NEP-18 [2]_,<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>NEP-13 [4]_ and NEP-30 [9]_ plus adding more protocols (not yet specified)<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>in addition to it. Even then, some parts of the NumPy API will remain<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>non-overridable, so it's a partial alternative.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>The main alternative to vendoring ``unumpy`` is to simply move it into NumPy<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>completely and not distribute it as a separate package. This would also achieve<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>the proposed goals, however we prefer to keep it a separate package for now,<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>for reasons already stated above.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>The third alternative is to move ``unumpy`` into the NumPy organisation and<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>develop it as a NumPy project. This will also achieve the said goals, and is<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>also a possibility that can be considered by this NEP. However, the act of<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>doing an extra ``pip install`` or ``conda install`` may discourage some users<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>from adopting this method.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>An alternative to requiring opt-in is mainly to *not* override ``np.asarray``<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>and ``np.array``, and making the rest of the NumPy API surface overridable,<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>instead providing ``np.duckarray`` and ``np.asduckarray``<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>as duck-array friendly alternatives that used the respective overrides. However,<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>this has the downside of adding a minor overhead to NumPy calls.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>Discussion<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>----------<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* ``uarray`` blogpost: https://labs.quansight.org/blog/2019/07/uarray-update-api-changes-overhead-and-comparison-to-__array_function__/<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* The discussion section of NEP-18: https://numpy.org/neps/nep-0018-array-function-protocol.html#discussion<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* NEP-22: https://numpy.org/neps/nep-0022-ndarray-duck-typing-overview.html<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* Dask issue #4462: https://github.com/dask/dask/issues/4462<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* PR #13046: https://github.com/numpy/numpy/pull/13046<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* Dask issue #4883: https://github.com/dask/dask/issues/4883<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* Issue #13831: https://github.com/numpy/numpy/issues/13831<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* Discussion PR 1: https://github.com/hameerabbasi/numpy/pull/3<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* Discussion PR 2: https://github.com/hameerabbasi/numpy/pull/4<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>* Discussion PR 3: https://github.com/numpy/numpy/pull/14389<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>References and Footnotes<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>------------------------<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>.. [1] uarray, A general dispatch mechanism for Python: https://uarray.readthedocs.io<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>.. [2] NEP 18 — A dispatch mechanism for NumPy’s high level array functions: https://numpy.org/neps/nep-0018-array-function-protocol.html<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>.. [3] NEP 22 — Duck typing for NumPy arrays – high level overview: https://numpy.org/neps/nep-0022-ndarray-duck-typing-overview.html<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>.. [4] NEP 13 — A Mechanism for Overriding Ufuncs: https://numpy.org/neps/nep-0013-ufunc-overrides.html<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>.. [5] Reply to Adding to the non-dispatched implementation of NumPy methods: http://numpy-discussion.10968.n7.nabble.com/Adding-to-the-non-dispatched-implementation-of-NumPy-methods-tp46816p46874.html<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>.. [6] Custom Dtype/Units discussion: http://numpy-discussion.10968.n7.nabble.com/Custom-Dtype-Units-discussion-td43262.html<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>.. [7] The epic dtype cleanup plan: https://github.com/numpy/numpy/issues/2899<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>.. [8] unumpy: NumPy, but implementation-independent: https://unumpy.readthedocs.io<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>.. [9] NEP 30 — Duck Typing for NumPy Arrays - Implementation: https://www.numpy.org/neps/nep-0030-duck-array-protocol.html<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>.. [10] http://scipy.github.io/devdocs/fft.html#backend-control<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>Copyright<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>---------<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='font-family:Courier;mso-fareast-language:EN-US'>This document has been placed in the public domain.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'><o:p> </o:p></span></p><div style='border:none;border-top:solid #B5C4DF 1.0pt;padding:3.0pt 0cm 0cm 0cm'><p class=MsoNormal><b><span style='font-size:12.0pt;color:black'>From: </span></b><span style='font-size:12.0pt;color:black'>NumPy-Discussion <numpy-discussion-bounces+hameerabbasi=yahoo.com@python.org> on behalf of Hameer Abbasi <einstein.edison@gmail.com><br><b>Reply to: </b>Discussion of Numerical Python <numpy-discussion@python.org><br><b>Date: </b>Thursday, 5. September 2019 at 17:12<br><b>To: </b><numpy-discussion@python.org><br><b>Subject: </b>Re: [Numpy-discussion] NEP 31 — Context-local and global overrides of the NumPy API<o:p></o:p></span></p></div><div><p class=MsoNormal><o:p> </o:p></p></div><p>Hello everyone;<o:p></o:p></p><p>Thanks to all the feedback from the community, in particular Sebastian Berg, we have a new draft of NEP-31.<o:p></o:p></p><p>Please find the full text quoted below for discussion and reference. Any feedback and discussion is welcome.<o:p></o:p></p><p><o:p> </o:p></p><pre>============================================================<o:p></o:p></pre><pre>NEP 31 — Context-local and global overrides of the NumPy API<o:p></o:p></pre><pre>============================================================<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>:Author: Hameer Abbasi <a href="mailto:habbasi@quansight.com"><habbasi@quansight.com></a><o:p></o:p></pre><pre>:Author: Ralf Gommers <a href="mailto:rgommers@quansight.com"><rgommers@quansight.com></a><o:p></o:p></pre><pre>:Author: Peter Bell <a href="mailto:pbell@quansight.com"><pbell@quansight.com></a><o:p></o:p></pre><pre>:Status: Draft<o:p></o:p></pre><pre>:Type: Standards Track<o:p></o:p></pre><pre>:Created: 2019-08-22<o:p></o:p></pre><pre><o:p> </o:p></pre><pre><o:p> </o:p></pre><pre>Abstract<o:p></o:p></pre><pre>--------<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>This NEP proposes to make all of NumPy's public API overridable via an<o:p></o:p></pre><pre>extensible backend mechanism.<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>Acceptance of this NEP means NumPy would provide global and context-local<o:p></o:p></pre><pre>overrides, as well as a dispatch mechanism similar to NEP-18 [2]_. First<o:p></o:p></pre><pre>experiences with ``__array_function__`` show that it is necessary to be able<o:p></o:p></pre><pre>to override NumPy functions that *do not take an array-like argument*, and<o:p></o:p></pre><pre>hence aren't overridable via ``__array_function__``. The most pressing need is<o:p></o:p></pre><pre>array creation and coercion functions, such as ``numpy.zeros`` or<o:p></o:p></pre><pre>``numpy.asarray``; see e.g. NEP-30 [9]_.<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>This NEP proposes to allow, in an opt-in fashion, overriding any part of the<o:p></o:p></pre><pre>NumPy API. It is intended as a comprehensive resolution to NEP-22 [3]_, and<o:p></o:p></pre><pre>obviates the need to add an ever-growing list of new protocols for each new<o:p></o:p></pre><pre>type of function or object that needs to become overridable.<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>Motivation and Scope<o:p></o:p></pre><pre>--------------------<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>The motivation behind ``uarray`` is manyfold: First, there have been several<o:p></o:p></pre><pre>attempts to allow dispatch of parts of the NumPy API, including (most<o:p></o:p></pre><pre>prominently), the ``__array_ufunc__`` protocol in NEP-13 [4]_, and the<o:p></o:p></pre><pre>``__array_function__`` protocol in NEP-18 [2]_, but this has shown the need<o:p></o:p></pre><pre>for further protocols to be developed, including a protocol for coercion (see<o:p></o:p></pre><pre>[5]_, [9]_). The reasons these overrides are needed have been extensively<o:p></o:p></pre><pre>discussed in the references, and this NEP will not attempt to go into the<o:p></o:p></pre><pre>details of why these are needed; but in short: It is necessary for library<o:p></o:p></pre><pre>authors to be able to coerce arbitrary objects into arrays of their own types,<o:p></o:p></pre><pre>such as CuPy needing to coerce to a CuPy array, for example, instead of<o:p></o:p></pre><pre>a NumPy array.<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>These kinds of overrides are useful for both the end-user as well as library<o:p></o:p></pre><pre>authors. End-users may have written or wish to write code that they then later<o:p></o:p></pre><pre>speed up or move to a different implementation, say PyData/Sparse. They can do<o:p></o:p></pre><pre>this simply by setting a backend. Library authors may also wish to write code<o:p></o:p></pre><pre>that is portable across array implementations, for example ``sklearn`` may wish<o:p></o:p></pre><pre>to write code for a machine learning algorithm that is portable across array<o:p></o:p></pre><pre>implementations while also using array creation functions.<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>This NEP takes a holistic approach: It assumes that there are parts of<o:p></o:p></pre><pre>the API that need to be overridable, and that these will grow over time. It<o:p></o:p></pre><pre>provides a general framework and a mechanism to avoid a design of a new<o:p></o:p></pre><pre>protocol each time this is required. This was the goal of ``uarray``: to<o:p></o:p></pre><pre>allow for overrides in an API without needing the design of a new protocol.<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>This NEP proposes the following: That ``unumpy`` [8]_  becomes the<o:p></o:p></pre><pre>recommended override mechanism for the parts of the NumPy API not yet covered<o:p></o:p></pre><pre>by ``__array_function__`` or ``__array_ufunc__``, and that ``uarray`` is<o:p></o:p></pre><pre>vendored into a new namespace within NumPy to give users and downstream<o:p></o:p></pre><pre>dependencies access to these overrides.  This vendoring mechanism is similar<o:p></o:p></pre><pre>to what SciPy decided to do for making ``scipy.fft`` overridable (see [10]_).<o:p></o:p></pre><pre><o:p> </o:p></pre><pre><o:p> </o:p></pre><pre>Detailed description<o:p></o:p></pre><pre>--------------------<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>Using overrides<o:p></o:p></pre><pre>~~~~~~~~~~~~~~~<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>The way we propose the overrides will be used by end users is::<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>    # On the library side<o:p></o:p></pre><pre>    import numpy.overridable as unp<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>    def library_function(array):<o:p></o:p></pre><pre>        array = unp.asarray(array)<o:p></o:p></pre><pre>        # Code using unumpy as usual<o:p></o:p></pre><pre>        return array<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>    # On the user side:<o:p></o:p></pre><pre>    import numpy.overridable as unp<o:p></o:p></pre><pre>    import uarray as ua<o:p></o:p></pre><pre>    import dask.array as da<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>    ua.register_backend(da)<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>    library_function(dask_array)  # works and returns dask_array<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>    with unp.set_backend(da):<o:p></o:p></pre><pre>        library_function([1, 2, 3, 4])  # actually returns a Dask array.<o:p></o:p></pre><pre><o:p> </o:p></pre><pre><o:p> </o:p></pre><pre>Here, ``backend`` can be any compatible object defined either by NumPy or an<o:p></o:p></pre><pre>external library, such as Dask or CuPy. Ideally, it should be the module<o:p></o:p></pre><pre>``dask.array`` or ``cupy`` itself.<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>Composing backends<o:p></o:p></pre><pre>~~~~~~~~~~~~~~~~~~<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>There are some backends which may depend on other backends, for example xarray<o:p></o:p></pre><pre>depending on `numpy.fft`, and transforming a time axis into a frequency axis,<o:p></o:p></pre><pre>or Dask/xarray holding an array other than a NumPy array inside it. This would<o:p></o:p></pre><pre>be handled in the following manner inside code::<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>    with ua.set_backend(cupy), ua.set_backend(dask.array):<o:p></o:p></pre><pre>        # Code that has distributed GPU arrays here<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>Proposals<o:p></o:p></pre><pre>~~~~~~~~~<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>The only change this NEP proposes at its acceptance, is to make ``unumpy`` the<o:p></o:p></pre><pre>officially recommended way to override NumPy. ``unumpy`` will remain a separate<o:p></o:p></pre><pre>repository/package (which we propose to vendor to avoid a hard dependency, and<o:p></o:p></pre><pre>use the separate ``unumpy`` package only if it is installed, rather than depend<o:p></o:p></pre><pre>on for the time being). In concrete terms, ``numpy.overridable`` becomes an<o:p></o:p></pre><pre>alias for ``unumpy``, if available with a fallback to the a vendored version if<o:p></o:p></pre><pre>not. ``uarray`` and ``unumpy`` and will be developed primarily with the input<o:p></o:p></pre><pre>of duck-array authors and secondarily, custom dtype authors, via the usual<o:p></o:p></pre><pre>GitHub workflow. There are a few reasons for this:<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>* Faster iteration in the case of bugs or issues.<o:p></o:p></pre><pre>* Faster design changes, in the case of needed functionality.<o:p></o:p></pre><pre>* ``unumpy`` will work with older versions of NumPy as well.<o:p></o:p></pre><pre>* The user and library author opt-in to the override process,<o:p></o:p></pre><pre>  rather than breakages happening when it is least expected.<o:p></o:p></pre><pre>  In simple terms, bugs in ``unumpy`` mean that ``numpy`` remains<o:p></o:p></pre><pre>  unaffected.<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>Advantanges of ``unumpy`` over other solutions<o:p></o:p></pre><pre>^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>``unumpy`` offers a number of advantanges over the approach of defining a new<o:p></o:p></pre><pre>protocol for every problem encountered: Whenever there is something requiring<o:p></o:p></pre><pre>an override, ``unumpy`` will be able to offer a unified API with very minor<o:p></o:p></pre><pre>changes. For example:<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>* ``ufunc`` objects can be overridden via their ``__call__``, ``reduce`` and<o:p></o:p></pre><pre>  other methods.<o:p></o:p></pre><pre>* Other functions can be overridden in a similar fashion.<o:p></o:p></pre><pre>* ``np.asduckarray`` goes away, and becomes ``np.overridable.asarray`` with a<o:p></o:p></pre><pre>  backend set.<o:p></o:p></pre><pre>* The same holds for array creation functions such as ``np.zeros``,<o:p></o:p></pre><pre>  ``np.empty`` and so on.<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>This also holds for the future: Making something overridable would require only<o:p></o:p></pre><pre>minor changes to ``unumpy``.<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>Another promise ``unumpy`` holds is one of default implementations. Default<o:p></o:p></pre><pre>implementations can be provided for any multimethod, in terms of others. This<o:p></o:p></pre><pre>allows one to override a large part of the NumPy API by defining only a small<o:p></o:p></pre><pre>part of it. This is to ease the creation of new duck-arrays, by providing<o:p></o:p></pre><pre>default implementations of many functions that can be easily expressed in<o:p></o:p></pre><pre>terms of others, as well as a repository of utility functions that help in the<o:p></o:p></pre><pre>implementation of duck-arrays that most duck-arrays would require.<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>It also allows one to override functions in a manner which<o:p></o:p></pre><pre>``__array_function__`` simply cannot, such as overriding ``np.einsum`` with the<o:p></o:p></pre><pre>version from the ``opt_einsum`` package, or Intel MKL overriding FFT, BLAS<o:p></o:p></pre><pre>or ``ufunc`` objects. They would define a backend with the appropriate<o:p></o:p></pre><pre>multimethods, and the user would select them via a ``with`` statement, or<o:p></o:p></pre><pre>registering them as a backend.<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>The last benefit is a clear way to coerce to a given backend (via the<o:p></o:p></pre><pre>``coerce`` keyword in ``ua.set_backend``), and a protocol<o:p></o:p></pre><pre>for coercing not only arrays, but also ``dtype`` objects and ``ufunc`` objects<o:p></o:p></pre><pre>with similar ones from other libraries. This is due to the existence of actual,<o:p></o:p></pre><pre>third party dtype packages, and their desire to blend into the NumPy ecosystem<o:p></o:p></pre><pre>(see [6]_). This is a separate issue compared to the C-level dtype redesign<o:p></o:p></pre><pre>proposed in [7]_, it's about allowing third-party dtype implementations to<o:p></o:p></pre><pre>work with NumPy, much like third-party array implementations. These can provide<o:p></o:p></pre><pre>features such as, for example, units, jagged arrays or other such features that<o:p></o:p></pre><pre>are outside the scope of NumPy.<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>Mixing NumPy and ``unumpy`` in the same file<o:p></o:p></pre><pre>^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>Normally, one would only want to import only one of ``unumpy`` or ``numpy``,<o:p></o:p></pre><pre>you would import it as ``np`` for familiarity. However, there may be situations<o:p></o:p></pre><pre>where one wishes to mix NumPy and the overrides, and there are a few ways to do<o:p></o:p></pre><pre>this, depending on the user's style::<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>    from numpy import overridable as unp<o:p></o:p></pre><pre>    import numpy as np<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>or::<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>    import numpy as np<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>    # Use unumpy via np.overridable<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>Duck-array coercion<o:p></o:p></pre><pre>~~~~~~~~~~~~~~~~~~~<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>There are inherent problems about returning objects that are not NumPy arrays<o:p></o:p></pre><pre>from ``numpy.array`` or ``numpy.asarray``, particularly in the context of C/C++<o:p></o:p></pre><pre>or Cython code that may get an object with a different memory layout than the<o:p></o:p></pre><pre>one it expects. However, we believe this problem may apply not only to these<o:p></o:p></pre><pre>two functions but all functions that return NumPy arrays. For this reason,<o:p></o:p></pre><pre>overrides are opt-in for the user, by using the submodule ``numpy.overridable``<o:p></o:p></pre><pre>rather than ``numpy``. NumPy will continue to work unaffected by anything in<o:p></o:p></pre><pre>``numpy.overridable``.<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>If the user wishes to obtain a NumPy array, there are two ways of doing it:<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>1. Use ``numpy.asarray`` (the non-overridable version).<o:p></o:p></pre><pre>2. Use ``numpy.overridable.asarray`` with the NumPy backend set and coercion<o:p></o:p></pre><pre>   enabled<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>Related Work<o:p></o:p></pre><pre>------------<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>Other override mechanisms<o:p></o:p></pre><pre>~~~~~~~~~~~~~~~~~~~~~~~~~<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>* NEP-18, the ``__array_function__`` protocol. [2]_<o:p></o:p></pre><pre>* NEP-13, the ``__array_ufunc__`` protocol. [3]_<o:p></o:p></pre><pre>* NEP-30, the ``__duck_array__`` protocol. [9]_<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>Existing NumPy-like array implementations<o:p></o:p></pre><pre>~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>* Dask: <a href="https://dask.org/">https://dask.org/</a><o:p></o:p></pre><pre>* CuPy: <a href="https://cupy.chainer.org/">https://cupy.chainer.org/</a><o:p></o:p></pre><pre>* PyData/Sparse: <a href="https://sparse.pydata.org/">https://sparse.pydata.org/</a><o:p></o:p></pre><pre>* Xnd: <a href="https://xnd.readthedocs.io/">https://xnd.readthedocs.io/</a><o:p></o:p></pre><pre>* Astropy's Quantity: <a href="https://docs.astropy.org/en/stable/units/">https://docs.astropy.org/en/stable/units/</a><o:p></o:p></pre><pre><o:p> </o:p></pre><pre>Existing and potential consumers of alternative arrays<o:p></o:p></pre><pre>~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>* Dask: <a href="https://dask.org/">https://dask.org/</a><o:p></o:p></pre><pre>* scikit-learn: <a href="https://scikit-learn.org/">https://scikit-learn.org/</a><o:p></o:p></pre><pre>* xarray: <a href="https://xarray.pydata.org/">https://xarray.pydata.org/</a><o:p></o:p></pre><pre>* TensorLy: <a href="http://tensorly.org/">http://tensorly.org/</a><o:p></o:p></pre><pre><o:p> </o:p></pre><pre>Existing alternate dtype implementations<o:p></o:p></pre><pre>~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>* ``ndtypes``: <a href="https://ndtypes.readthedocs.io/en/latest/">https://ndtypes.readthedocs.io/en/latest/</a><o:p></o:p></pre><pre>* Datashape: <a href="https://datashape.readthedocs.io">https://datashape.readthedocs.io</a><o:p></o:p></pre><pre>* Plum: <a href="https://plum-py.readthedocs.io/">https://plum-py.readthedocs.io/</a><o:p></o:p></pre><pre><o:p> </o:p></pre><pre>Implementation<o:p></o:p></pre><pre>--------------<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>The implementation of this NEP will require the following steps:<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>* Implementation of ``uarray`` multimethods corresponding to the<o:p></o:p></pre><pre>  NumPy API, including classes for overriding ``dtype``, ``ufunc``<o:p></o:p></pre><pre>  and ``array`` objects, in the ``unumpy`` repository.<o:p></o:p></pre><pre>* Moving backends from ``unumpy`` into the respective array libraries.<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>``uarray`` Primer<o:p></o:p></pre><pre>~~~~~~~~~~~~~~~~~<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>**Note:** *This section will not attempt to go into too much detail about<o:p></o:p></pre><pre>uarray, that is the purpose of the uarray documentation.* [1]_<o:p></o:p></pre><pre>*However, the NumPy community will have input into the design of<o:p></o:p></pre><pre>uarray, via the issue tracker.*<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>``unumpy`` is the interface that defines a set of overridable functions<o:p></o:p></pre><pre>(multimethods) compatible with the numpy API. To do this, it uses the<o:p></o:p></pre><pre>``uarray`` library. ``uarray`` is a general purpose tool for creating<o:p></o:p></pre><pre>multimethods that dispatch to one of multiple different possible backend<o:p></o:p></pre><pre>implementations. In this sense, it is similar to the ``__array_function__``<o:p></o:p></pre><pre>protocol but with the key difference that the backend is explicitly installed<o:p></o:p></pre><pre>by the end-user and not coupled into the array type.<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>Decoupling the backend from the array type gives much more flexibility to<o:p></o:p></pre><pre>end-users and backend authors. For example, it is possible to:<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>* override functions not taking arrays as arguments<o:p></o:p></pre><pre>* create backends out of source from the array type<o:p></o:p></pre><pre>* install multiple backends for the same array type<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>This decoupling also means that ``uarray`` is not constrained to dispatching<o:p></o:p></pre><pre>over array-like types. The backend is free to inspect the entire set of<o:p></o:p></pre><pre>function arguments to determine if it can implement the function e.g. ``dtype``<o:p></o:p></pre><pre>parameter dispatching.<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>Defining backends<o:p></o:p></pre><pre>^^^^^^^^^^^^^^^^^<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>``uarray`` consists of two main protocols: ``__ua_convert__`` and<o:p></o:p></pre><pre>``__ua_function__``, called in that order, along with ``__ua_domain__``.<o:p></o:p></pre><pre>``__ua_convert__`` is for conversion and coercion. It has the signature<o:p></o:p></pre><pre>``(dispatchables, coerce)``, where ``dispatchables`` is an iterable of<o:p></o:p></pre><pre>``ua.Dispatchable`` objects and ``coerce`` is a boolean indicating whether or<o:p></o:p></pre><pre>not to force the conversion. ``ua.Dispatchable`` is a simple class consisting<o:p></o:p></pre><pre>of three simple values: ``type``, ``value``, and ``coercible``.<o:p></o:p></pre><pre>``__ua_convert__`` returns an iterable of the converted values, or<o:p></o:p></pre><pre>``NotImplemented`` in the case of failure.<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>``__ua_function__`` has the signature ``(func, args, kwargs)`` and defines<o:p></o:p></pre><pre>the actual implementation of the function. It recieves the function and its<o:p></o:p></pre><pre>arguments. Returning ``NotImplemented`` will cause a move to the default<o:p></o:p></pre><pre>implementation of the function if one exists, and failing that, the next<o:p></o:p></pre><pre>backend.<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>Here is what will happen assuming a ``uarray`` multimethod is called:<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>1. We canonicalise the arguments so any arguments without a default<o:p></o:p></pre><pre>   are placed in ``*args`` and those with one are placed in ``**kwargs``.<o:p></o:p></pre><pre>2. We check the list of backends.<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>   a. If it is empty, we try the default implementation.<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>3. We check if the backend's ``__ua_convert__`` method exists. If it exists:<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>   a. We pass it the output of the dispatcher,<o:p></o:p></pre><pre>      which is an iterable of ``ua.Dispatchable`` objects.<o:p></o:p></pre><pre>   b. We feed this output, along with the arguments,<o:p></o:p></pre><pre>      to the argument replacer. ``NotImplemented`` means we move to 3<o:p></o:p></pre><pre>      with the next backend.<o:p></o:p></pre><pre>   c. We store the replaced arguments as the new arguments.<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>4. We feed the arguments into ``__ua_function__``, and return the output, and<o:p></o:p></pre><pre>   exit if it isn't ``NotImplemented``.<o:p></o:p></pre><pre>5. If the default implementation exists, we try it with the current backend.<o:p></o:p></pre><pre>6. On failure,  we move to 3 with the next backend. If there are no more<o:p></o:p></pre><pre>   backends, we move to 7.<o:p></o:p></pre><pre>7. We raise a ``ua.BackendNotImplementedError``.<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>Defining overridable multimethods<o:p></o:p></pre><pre>^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>To define an overridable function (a multimethod), one needs a few things:<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>1. A dispatcher that returns an iterable of ``ua.Dispatchable`` objects.<o:p></o:p></pre><pre>2. A reverse dispatcher that replaces dispatchable values with the supplied<o:p></o:p></pre><pre>   ones.<o:p></o:p></pre><pre>3. A domain.<o:p></o:p></pre><pre>4. Optionally, a default implementation, which can be provided in terms of<o:p></o:p></pre><pre>   other multimethods.<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>As an example, consider the following::<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>    import uarray as ua<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>    def full_argreplacer(args, kwargs, dispatchables):<o:p></o:p></pre><pre>        def full(shape, fill_value, dtype=None, order='C'):<o:p></o:p></pre><pre>            return (shape, fill_value), dict(<o:p></o:p></pre><pre>                dtype=dispatchables[0],<o:p></o:p></pre><pre>                order=order<o:p></o:p></pre><pre>            )<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>        return full(*args, **kwargs)<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>    @ua.create_multimethod(full_argreplacer, domain="numpy")<o:p></o:p></pre><pre>    def full(shape, fill_value, dtype=None, order='C'):<o:p></o:p></pre><pre>        return (ua.Dispatchable(dtype, np.dtype),)<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>A large set of examples can be found in the ``unumpy`` repository, [8]_.<o:p></o:p></pre><pre>This simple act of overriding callables allows us to override:<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>* Methods<o:p></o:p></pre><pre>* Properties, via ``fget`` and ``fset``<o:p></o:p></pre><pre>* Entire objects, via ``__get__``.<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>Examples for NumPy<o:p></o:p></pre><pre>^^^^^^^^^^^^^^^^^^<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>A library that implements a NumPy-like API will use it in the following<o:p></o:p></pre><pre>manner (as an example)::<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>    import numpy.overridable as unp<o:p></o:p></pre><pre>    _ua_implementations = {}<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>    __ua_domain__ = "numpy"<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>    def __ua_function__(func, args, kwargs):<o:p></o:p></pre><pre>        fn = _ua_implementations.get(func, None)<o:p></o:p></pre><pre>        return fn(*args, **kwargs) if fn is not None else NotImplemented<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>    def implements(ua_func):<o:p></o:p></pre><pre>        def inner(func):<o:p></o:p></pre><pre>            _ua_implementations[ua_func] = func<o:p></o:p></pre><pre>            return func<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>        return inner<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>    @implements(unp.asarray)<o:p></o:p></pre><pre>    def asarray(a, dtype=None, order=None):<o:p></o:p></pre><pre>        # Code here<o:p></o:p></pre><pre>        # Either this method or __ua_convert__ must<o:p></o:p></pre><pre>        # return NotImplemented for unsupported types,<o:p></o:p></pre><pre>        # Or they shouldn't be marked as dispatchable.<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>    # Provides a default implementation for ones and zeros.<o:p></o:p></pre><pre>    @implements(unp.full)<o:p></o:p></pre><pre>    def full(shape, fill_value, dtype=None, order='C'):<o:p></o:p></pre><pre>        # Code here<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>Backward compatibility<o:p></o:p></pre><pre>----------------------<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>There are no backward incompatible changes proposed in this NEP.<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>Alternatives<o:p></o:p></pre><pre>------------<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>The current alternative to this problem is a combination of NEP-18 [2]_,<o:p></o:p></pre><pre>NEP-13 [4]_ and NEP-30 [9]_ plus adding more protocols (not yet specified)<o:p></o:p></pre><pre>in addition to it. Even then, some parts of the NumPy API will remain<o:p></o:p></pre><pre>non-overridable, so it's a partial alternative.<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>The main alternative to vendoring ``unumpy`` is to simply move it into NumPy<o:p></o:p></pre><pre>completely and not distribute it as a separate package. This would also achieve<o:p></o:p></pre><pre>the proposed goals, however we prefer to keep it a separate package for now,<o:p></o:p></pre><pre>for reasons already stated above.<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>The third alternative is to move ``unumpy`` into the NumPy organisation and<o:p></o:p></pre><pre>develop it as a NumPy project. This will also achieve the said goals, and is<o:p></o:p></pre><pre>also a possibility that can be considered by this NEP. However, the act of<o:p></o:p></pre><pre>doing an extra ``pip install`` or ``conda install`` may discourage some users<o:p></o:p></pre><pre>from adopting this method.<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>Discussion<o:p></o:p></pre><pre>----------<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>* ``uarray`` blogpost: <a href="https://labs.quansight.org/blog/2019/07/uarray-update-api-changes-overhead-and-comparison-to-__array_function__/">https://labs.quansight.org/blog/2019/07/uarray-update-api-changes-overhead-and-comparison-to-__array_function__/</a><o:p></o:p></pre><pre>* The discussion section of NEP-18: <a href="https://numpy.org/neps/nep-0018-array-function-protocol.html#discussion">https://numpy.org/neps/nep-0018-array-function-protocol.html#discussion</a><o:p></o:p></pre><pre>* NEP-22: <a href="https://numpy.org/neps/nep-0022-ndarray-duck-typing-overview.html">https://numpy.org/neps/nep-0022-ndarray-duck-typing-overview.html</a><o:p></o:p></pre><pre>* Dask issue #4462: <a href="https://github.com/dask/dask/issues/4462">https://github.com/dask/dask/issues/4462</a><o:p></o:p></pre><pre>* PR #13046: <a href="https://github.com/numpy/numpy/pull/13046">https://github.com/numpy/numpy/pull/13046</a><o:p></o:p></pre><pre>* Dask issue #4883: <a href="https://github.com/dask/dask/issues/4883">https://github.com/dask/dask/issues/4883</a><o:p></o:p></pre><pre>* Issue #13831: <a href="https://github.com/numpy/numpy/issues/13831">https://github.com/numpy/numpy/issues/13831</a><o:p></o:p></pre><pre>* Discussion PR 1: <a href="https://github.com/hameerabbasi/numpy/pull/3">https://github.com/hameerabbasi/numpy/pull/3</a><o:p></o:p></pre><pre>* Discussion PR 2: <a href="https://github.com/hameerabbasi/numpy/pull/4">https://github.com/hameerabbasi/numpy/pull/4</a><o:p></o:p></pre><pre>* Discussion PR 3: <a href="https://github.com/numpy/numpy/pull/14389">https://github.com/numpy/numpy/pull/14389</a><o:p></o:p></pre><pre><o:p> </o:p></pre><pre><o:p> </o:p></pre><pre>References and Footnotes<o:p></o:p></pre><pre>------------------------<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>.. [1] uarray, A general dispatch mechanism for Python: <a href="https://uarray.readthedocs.io">https://uarray.readthedocs.io</a><o:p></o:p></pre><pre><o:p> </o:p></pre><pre>.. [2] NEP 18 — A dispatch mechanism for NumPy’s high level array functions: <a href="https://numpy.org/neps/nep-0018-array-function-protocol.html">https://numpy.org/neps/nep-0018-array-function-protocol.html</a><o:p></o:p></pre><pre><o:p> </o:p></pre><pre>.. [3] NEP 22 — Duck typing for NumPy arrays – high level overview: <a href="https://numpy.org/neps/nep-0022-ndarray-duck-typing-overview.html">https://numpy.org/neps/nep-0022-ndarray-duck-typing-overview.html</a><o:p></o:p></pre><pre><o:p> </o:p></pre><pre>.. [4] NEP 13 — A Mechanism for Overriding Ufuncs: <a href="https://numpy.org/neps/nep-0013-ufunc-overrides.html">https://numpy.org/neps/nep-0013-ufunc-overrides.html</a><o:p></o:p></pre><pre><o:p> </o:p></pre><pre>.. [5] Reply to Adding to the non-dispatched implementation of NumPy methods: <a href="http://numpy-discussion.10968.n7.nabble.com/Adding-to-the-non-dispatched-implementation-of-NumPy-methods-tp46816p46874.html">http://numpy-discussion.10968.n7.nabble.com/Adding-to-the-non-dispatched-implementation-of-NumPy-methods-tp46816p46874.html</a><o:p></o:p></pre><pre><o:p> </o:p></pre><pre>.. [6] Custom Dtype/Units discussion: <a href="http://numpy-discussion.10968.n7.nabble.com/Custom-Dtype-Units-discussion-td43262.html">http://numpy-discussion.10968.n7.nabble.com/Custom-Dtype-Units-discussion-td43262.html</a><o:p></o:p></pre><pre><o:p> </o:p></pre><pre>.. [7] The epic dtype cleanup plan: <a href="https://github.com/numpy/numpy/issues/2899">https://github.com/numpy/numpy/issues/2899</a><o:p></o:p></pre><pre><o:p> </o:p></pre><pre>.. [8] unumpy: NumPy, but implementation-independent: <a href="https://unumpy.readthedocs.io">https://unumpy.readthedocs.io</a><o:p></o:p></pre><pre><o:p> </o:p></pre><pre>.. [9] NEP 30 — Duck Typing for NumPy Arrays - Implementation: <a href="https://www.numpy.org/neps/nep-0030-duck-array-protocol.html">https://www.numpy.org/neps/nep-0030-duck-array-protocol.html</a><o:p></o:p></pre><pre><o:p> </o:p></pre><pre>.. [10] <a href="http://scipy.github.io/devdocs/fft.html#backend-control">http://scipy.github.io/devdocs/fft.html#backend-control</a><o:p></o:p></pre><pre><o:p> </o:p></pre><pre><o:p> </o:p></pre><pre>Copyright<o:p></o:p></pre><pre>---------<o:p></o:p></pre><pre><o:p> </o:p></pre><pre>This document has been placed in the public domain.<o:p></o:p></pre><p class=MsoNormal>_______________________________________________ NumPy-Discussion mailing list NumPy-Discussion@python.org https://mail.python.org/mailman/listinfo/numpy-discussion <o:p></o:p></p></div></body></html>