Skip to content

Commit f522cbf

Browse files
committed
Revise NEP-31.
1 parent 2246f68 commit f522cbf

File tree

1 file changed

+112
-1
lines changed

1 file changed

+112
-1
lines changed

doc/neps/nep-0031-uarray.rst

Lines changed: 112 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,10 @@ GitHub workflow. There are a few reasons for this:
130130
rather than breakages happening when it is least expected.
131131
In simple terms, bugs in ``unumpy`` mean that ``numpy`` remains
132132
unaffected.
133+
* For ``numpy.fft``, ``numpy.linalg`` and ``numpy.random``, the functions in
134+
the main namespace will mirror those in the ``numpy.overridable`` namespace.
135+
The reason for this is that there may exist functions in the in these
136+
submodules that need backends, even for ``numpy.ndarray`` inputs.
133137

134138
Advantanges of ``unumpy`` over other solutions
135139
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -156,7 +160,12 @@ allows one to override a large part of the NumPy API by defining only a small
156160
part of it. This is to ease the creation of new duck-arrays, by providing
157161
default implementations of many functions that can be easily expressed in
158162
terms of others, as well as a repository of utility functions that help in the
159-
implementation of duck-arrays that most duck-arrays would require.
163+
implementation of duck-arrays that most duck-arrays would require. This would
164+
allow us to avoid designing entire protocols, e.g., a protocol for stacking
165+
and concatenating would be replaced by simply implementing ``stack`` and/or
166+
``concatenate`` and then providing default implementations for everything else
167+
in that class. The same applies for transposing, and many other functions
168+
which cannot even be concretely covered by protocols.
160169

161170
It also allows one to override functions in a manner which
162171
``__array_function__`` simply cannot, such as overriding ``np.einsum`` with the
@@ -211,6 +220,94 @@ If the user wishes to obtain a NumPy array, there are two ways of doing it:
211220
2. Use ``numpy.overridable.asarray`` with the NumPy backend set and coercion
212221
enabled
213222

223+
Aliases outside of the ``numpy.overridable`` namespace
224+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
225+
226+
All functionality in ``numpy.random``, ``numpy.linalg`` and ``numpy.fft``
227+
will be aliased to their respective overridable versions inside
228+
``numpy.overridable``. The reason for this is that there are alternative
229+
implementations of RNGs (``mkl-random``), linear algebra routines (``eigen``,
230+
``blis``) and FFT routines (``mkl-fft``, ``pyFFTW``) that need to operate on
231+
``numpy.ndarray`` inputs, but still need the ability to switch behaviour.
232+
233+
This is different from monkeypatching in a few different ways:
234+
235+
* The caller-facing signature of the function is always the same,
236+
so there is at least the loose sense of an API contract. Monkeypatching
237+
does not provide this ability.
238+
* There is the ability of locally switching the backend.
239+
* It has been `suggested <http://numpy-discussion.10968.n7.nabble.com/NEP-31-Context-local-and-global-overrides-of-the-NumPy-API-tp47452p47472.html>`_
240+
that the reason that 1.17 hasn't landed in the Anaconda defaults channel is
241+
due to the incompatibility between monkeypatching and `__array_function__`.
242+
243+
All this isn't possible at all with ``__array_function__`` or
244+
``__array_ufunc__``.
245+
246+
It has been formally realised (at least in part) that a backend system is
247+
needed for this, in the `NumPy roadmap <https://numpy.org/neps/roadmap.html#other-functionality>`_.
248+
249+
For ``numpy.random``, it's still necessary to make the C-API fit the one
250+
proposed in `NEP-19 <https://numpy.org/neps/nep-0019-rng-policy.html>`_.
251+
This is impossible for `mkl-random`, because then it would need to be
252+
rewritten to fit that framework. The general guarantees on stream
253+
compatibility will be the same as before: If there's a backend that affects
254+
``numpy.random`` set, we make no guarantees about stream compatibility, and it
255+
is up to the backend author to provide their own guarantees.
256+
257+
Providing a way for implicit dispatch
258+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
259+
260+
It has been suggested that the ability to dispatch methods which do not take
261+
a dispatchable is needed, while guessing that backend from another dispatchable.
262+
263+
As a concrete example, consider the following:
264+
265+
.. code:: python
266+
267+
with unumpy.determine_backend(array_like, np.ndarray):
268+
unumpy.arange(len(array_like))
269+
270+
While this does not exist yet in ``uarray``, it is trivial to add it. The
271+
answer is to simply call ``__ua_convert__`` on the passed-in array with
272+
``coerce=False`` for each backend, and comparing the result to
273+
``NotImplemented``.
274+
275+
The need for an opt-in module
276+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
277+
278+
The need for an opt-in module is realised because of a few reasons:
279+
280+
* There are parts of the API (like `numpy.asarray`) that simply cannot be
281+
overridden due to incompatibility concerns with C/Cython extensions, however,
282+
one may want to coerce to a duck-array using ``asarray`` with a backend set.
283+
* There are possible issues around an implicit option and monkeypatching.
284+
285+
NEP 18 notes that this may require maintenance of two separate APIs. However,
286+
this burden may be lessened by, for example, parametrizing all tests over
287+
``numpy.overridable`` separately via a fixture. This also has the side-effect
288+
of thoroughly testing it, unlike ``__array_function__``. We also feel that it
289+
provides an oppurtunity to separate the NumPy API contract properly from the
290+
implementation.
291+
292+
Benefits to end-users and mixing backends
293+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
294+
295+
Mixing backends is easy in ``uarray``, one only has to do:
296+
297+
.. code:: python
298+
299+
# Explicitly say which backends you want to mix
300+
ua.register_backend(backend1)
301+
ua.register_backend(backend2)
302+
ua.register_backend(backend3)
303+
304+
# Freely use code that mixes backends here.
305+
306+
The benefits to end-users extend beyond just writing new code. Old code
307+
(usually in the form of scripts) can be easily ported to different backends
308+
by a simple import switch and a line adding the preferred backend. This way,
309+
users may find it easier to port existing code to GPU or distributed computing.
310+
214311
Related Work
215312
------------
216313

@@ -245,6 +342,14 @@ Existing alternate dtype implementations
245342
* Datashape: https://datashape.readthedocs.io
246343
* Plum: https://plum-py.readthedocs.io/
247344

345+
Alternate implementations of parts of the NumPy API
346+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
347+
348+
* ``mkl_random``: https://github.com/IntelPython/mkl_random
349+
* ``mkl_fft``: https://github.com/IntelPython/mkl_fft
350+
* ``bottleneck``: https://github.com/pydata/bottleneck
351+
* ``opt_einsum``: https://github.com/dgasmith/opt_einsum
352+
248353
Implementation
249354
--------------
250355

@@ -420,6 +525,12 @@ also a possibility that can be considered by this NEP. However, the act of
420525
doing an extra ``pip install`` or ``conda install`` may discourage some users
421526
from adopting this method.
422527

528+
An alternative to requiring opt-in is mainly to *not* override ``np.asarray``
529+
and ``np.array``, and making the rest of the NumPy API surface overridable,
530+
instead providing ``np.duckarray`` and ``np.asduckarray``
531+
as duck-array friendly alternatives that used the respective overrides. However,
532+
this has the downside of adding a minor overhead to NumPy calls.
533+
423534
Discussion
424535
----------
425536

0 commit comments

Comments
 (0)