diff options
author | Ralf Gommers <ralf.gommers@gmail.com> | 2021-04-17 14:38:54 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-04-17 14:38:54 +0200 |
commit | ec227f068f4a9f5036b02ea68ca8a419f2785cbe (patch) | |
tree | e4c9bc16265b744572755c3ffd0c0956dc24a6f7 | |
parent | 038337a410cbfcc93ffb2c7d0d9748d0e72c60d9 (diff) | |
parent | 90a40895d78ab793e087b8433298903966643816 (diff) | |
download | numpy-ec227f068f4a9f5036b02ea68ca8a419f2785cbe.tar.gz |
Merge pull request #18761 from seberg/small-fixups-nep43
DOC: Small fixes (including formatting) for NEP 43
-rw-r--r-- | doc/neps/nep-0043-extensible-ufuncs.rst | 55 |
1 files changed, 28 insertions, 27 deletions
diff --git a/doc/neps/nep-0043-extensible-ufuncs.rst b/doc/neps/nep-0043-extensible-ufuncs.rst index 6442b9b03..3c6407728 100644 --- a/doc/neps/nep-0043-extensible-ufuncs.rst +++ b/doc/neps/nep-0043-extensible-ufuncs.rst @@ -337,12 +337,12 @@ steps involved in a call to a universal function in NumPy. A UFunc call is split into the following steps: -1. *Handle ``__array_ufunc__`` protocol:* +1. Handle ``__array_ufunc__`` protocol: * For array-likes such as a Dask arrays, NumPy can defer the operation. This step is performed first, and unaffected by this NEP (compare :ref:`NEP18`). -2. *Promotion and dispatching* +2. Promotion and dispatching * Given the DTypes of all inputs, find the correct implementation. E.g. an implementation for ``float64``, ``int64`` or a user-defined DType. @@ -351,7 +351,7 @@ A UFunc call is split into the following steps: For example, adding a ``float32`` and a ``float64`` is implemented by first casting the ``float32`` to ``float64``. -3. *Parametric ``dtype`` resolution:* +3. Parametric ``dtype`` resolution: * In general, whenever an output DType is parametric the parameters have to be found (resolved). @@ -362,15 +362,16 @@ A UFunc call is split into the following steps: which fills in the default dtype instances (ensuring for example native byte order). -4. *Preparing the iteration:* +4. Preparing the iteration: * This step is largely handled by ``NpyIter`` internally (the iterator). * Allocate all outputs and temporary buffers necessary to perform casts. + This *requires* the dtypes as resolved in step 3. * Find the best iteration order, which includes information to efficiently implement broadcasting. For example, adding a single value to an array repeats the same value. -5. *Setup and fetch the C-level function:* +5. Setup and fetch the C-level function: * If necessary, allocate temporary working space. * Find the C-implemented, light weight, inner-loop function. @@ -383,33 +384,35 @@ A UFunc call is split into the following steps: the GIL may be released (to allow threading). * Clear floating point exception flags. -6. *Perform the actual calculation:* +6. Perform the actual calculation: * Run the DType specific inner-loop function. * The inner-loop may require access to additional data, such as dtypes or additional data set in the previous step. * The inner-loop function may be called an undefined number of times. -7. *Finalize:* +7. Finalize: - * Free any temporary working space allocated in 5. + * Free any temporary working space allocated in step 5. * Check for floating point exception flags. * Return the result. The ``ArrayMethod`` provides a concept to group steps 3 to 6 and partially 7. -However, implementers of a new ufunc or ``ArrayMethod`` do not need to -customize the behaviour in steps 4 or 6, aside from the inner-loop function. -For the ``ArrayMethod`` implementer, the central steps to have control over -are step 3 and step 5 to provide the custom inner-loop function. -Further customization is a potential future extension. +However, implementers of a new ufunc or ``ArrayMethod`` usually do not need to +customize the behaviour in steps 4 or 6 which NumPy can and does provide. +For the ``ArrayMethod`` implementer, the central steps to customize +are step 3 and step 5. These provide the custom inner-loop function and +potentially inner-loop specific setup. +Further customization is possible and anticipated as future extensions. -Step 2. is promotion and dispatching which will also be restructured -with new API which allows influencing the process where necessary. +Step 2. is promotion and dispatching and will be restructured +with new API to allow customization of the process where necessary. Step 1 is listed for completeness and is unaffected by this NEP. The following sketch provides an overview of step 2 to 6 with an emphasize -of how dtypes are handled: +of how dtypes are handled and which parts are customizable ("Registered") +and which are handled by NumPy: .. figure:: _static/nep43-sketch.svg :figclass: align-center @@ -425,7 +428,6 @@ We use the ``class`` syntax to describe the information required to create a new ``ArrayMethod`` object: .. code-block:: python - :dedent: 0 class ArrayMethod: name: str # Name, mainly useful for debugging @@ -470,7 +472,6 @@ a new ``ArrayMethod`` object: With ``Context`` providing mostly static information about the function call: .. code-block:: python - :dedent: 0 class Context: # The ArrayMethod object itself: @@ -660,7 +661,7 @@ definitions (see also :ref:`NEP 42 <NEP42>` ``CastingImpl``): for example for implementing warnings (see Error and Warning Handling below). To simplify this NumPy will pass a single zero initialized ``npy_intp *`` when ``user_data`` is not set. - *NOTE that it would be possible to pass this as part of ``Context``.* + *Note that it would be possible to pass this as part of Context.* * The optional ``get_loop`` function will not be public initially, to avoid finalizing the API which requires design choices also with casting: @@ -757,7 +758,7 @@ While the returned casting-safety (``NPY_CASTING``) will almost always be cast and a custom error will be set. (This distinction is important for the ``np.can_cast()`` function, which should raise the first one and return ``False`` in the second case, it is not noteworthy for typical ufuncs). - *This point is under consideration, we may use ``-1`` to indicate + *This point is under consideration, we may use -1 to indicate a general error, and use a different return value for an impossible cast.* * Returning the casting safety is central to NEP 42 for casting and allows the unmodified use of ``ArrayMethod`` there. @@ -794,7 +795,7 @@ Extending the inner-loop signature Extending the inner-loop signature is another central and necessary part of the NEP. -**Passing in the ``Context``:** +**Passing in the Context:** Passing in the ``Context`` potentially allows for the future extension of the signature by adding new fields to the context struct. @@ -822,8 +823,8 @@ exists in NumPy for this purpose, it seems a natural choice. To simplify some use-cases (see "Error Handling" below), we will pass a ``npy_intp *innerloop_data = 0`` instead when ``innerloop_data`` is not provided. -*Note: Since ``get_loop`` is expected to be private initially we can gain -experience with ``innerloop_data`` before exposing it as public API.* +*Note:* Since ``get_loop`` is expected to be private initially we can gain +experience with ``innerloop_data`` before exposing it as public API. **Return value:** @@ -954,7 +955,7 @@ This wrapped ``ArrayMethod`` will have two additional methods: have changed the byte-order), and further resolve the physical unit making the final signature:: - ``Unit[Float64]("m") + Unit[Float64]("m") -> Unit[Float64]("m")`` + Unit[Float64]("m") + Unit[Float64]("m") -> Unit[Float64]("m") the UFunc machinery will take care of casting the "km" input to "m". @@ -1039,8 +1040,8 @@ In the future we expect that ``ArrayMethod``\ s can also be defined for **Promotion:** If a matching ``ArrayMethod`` exists, dispatching is straight forward. -However, when it does not, require additional definitions to implement -promotion: +However, when it does not, additional definitions are required to implement +this "promotion": * By default any UFunc has a promotion which uses the common DType of all inputs and dispatches a second time. This is well-defined for most @@ -1085,7 +1086,7 @@ In this case, just as a ``Timedelta64 * int64`` and ``int64 * timedelta64`` ``ArrayMethod`` is necessary, a second promoter will have to be registered to handle the case where the integer is passed first. -**Dispatching rules for ``ArrayMethod`` and Promoters:** +**Dispatching rules for ArrayMethod and Promoters:** Promoter and ``ArrayMethod`` are discovered by finding the best match as defined by the DType class hierarchy. |