diff options
Diffstat (limited to 'numpy')
| -rw-r--r-- | numpy/core/include/numpy/ufuncobject.h | 7 | ||||
| -rw-r--r-- | numpy/core/src/umath/dispatching.c | 56 | ||||
| -rw-r--r-- | numpy/core/src/umath/legacy_array_method.c | 13 |
3 files changed, 43 insertions, 33 deletions
diff --git a/numpy/core/include/numpy/ufuncobject.h b/numpy/core/include/numpy/ufuncobject.h index cf14eb82e..fd7307703 100644 --- a/numpy/core/include/numpy/ufuncobject.h +++ b/numpy/core/include/numpy/ufuncobject.h @@ -211,12 +211,11 @@ typedef struct _tagPyUFuncObject { /* Identity for reduction, when identity == PyUFunc_IdentityValue */ PyObject *identity_value; + /* New in NPY_API_VERSION 0x0000000F and above */ + /* New private fields related to dispatching */ void *_dispatch_cache; - /* - * Currently, just a list, but that can never allow deletion (unless - * elements that may get deleted again are deleted occasionally). - */ + /* A PyListObject of `(tuple of DTypes, ArrayMethod/Promoter)` */ PyObject *_loops; } PyUFuncObject; diff --git a/numpy/core/src/umath/dispatching.c b/numpy/core/src/umath/dispatching.c index 6c8c9d9e4..c1867563a 100644 --- a/numpy/core/src/umath/dispatching.c +++ b/numpy/core/src/umath/dispatching.c @@ -20,7 +20,7 @@ * steps: * * 1. Override any `operand_DTypes` from `signature`. - * 2. Check if the new `operand_Dtypes` is cached (got to 4. if it is) + * 2. Check if the new `operand_Dtypes` is cached (if it is, got to 4.) * 3. Find the best matching "loop". This is done using multiple dispatching * on all `operand_DTypes` and loop `dtypes`. A matching loop must be * one whose DTypes are superclasses of the `operand_DTypes` (that are @@ -130,7 +130,6 @@ resolve_implementation_info(PyUFuncObject *ufunc, PyArray_DTypeMeta *op_dtypes[], PyObject **out_info) { int nin = ufunc->nin, nargs = ufunc->nargs; - /* Use new style type resolution has to happen... */ Py_ssize_t size = PySequence_Length(ufunc->_loops); PyObject *best_dtypes = NULL; PyObject *best_resolver_info = NULL; @@ -233,7 +232,10 @@ resolve_implementation_info(PyUFuncObject *ufunc, } /* * TODO: The `abstract` paths and all subclass checks are not - * actually used right now. This will b + * used right now. These will be used when user DTypes + * and promoters are used. Especially, the abstract + * paths will become important when value-based promotion + * is removed from NumPy. */ if (is_not_specified) { /* @@ -367,7 +369,7 @@ resolve_implementation_info(PyUFuncObject *ufunc, if (current_best == -1) { /* * TODO: It would be nice to have a "diagnostic mode" that - * informs if this happens! (An immediate error current + * informs if this happens! (An immediate error currently * blocks later legacy resolution, but may work in the * future.) */ @@ -419,11 +421,12 @@ call_promoter_and_recurse( /* - * Used for the legacy fallback promotion when `signature` or `dtype` is - * provided. - * We do not need to pass the type tuple when we use the legacy path - * for type resolution rather than promotion; the old system did not - * differentiate between these two concepts. + * Convert the DType `signature` into the tuple of descriptors that is used + * by the old ufunc type resolvers in `ufunc_type_resolution.c`. + * + * Note that we do not need to pass the type tuple when we use the legacy path + * for type resolution rather than promotion, since the signature is always + * correct in that case. */ static int _make_new_typetup( @@ -478,8 +481,7 @@ legacy_promote_using_legacy_type_resolver(PyUFuncObject *ufunc, PyArray_DTypeMeta *operation_DTypes[], int *out_cacheable) { int nargs = ufunc->nargs; - PyArray_Descr *out_descrs[NPY_MAXARGS]; - memset(out_descrs, 0, nargs * sizeof(*out_descrs)); + PyArray_Descr *out_descrs[NPY_MAXARGS] = {NULL}; PyObject *type_tuple = NULL; if (_make_new_typetup(nargs, signature, &type_tuple) < 0) { @@ -501,7 +503,6 @@ legacy_promote_using_legacy_type_resolver(PyUFuncObject *ufunc, Py_XDECREF(type_tuple); for (int i = 0; i < nargs; i++) { - // TODO: Check that this is correct with INCREF: Stop using borrowed references! (Then this is actually correct) operation_DTypes[i] = NPY_DTYPE(out_descrs[i]); Py_INCREF(operation_DTypes[i]); Py_DECREF(out_descrs[i]); @@ -558,11 +559,13 @@ add_and_return_legacy_wrapping_ufunc_loop(PyUFuncObject *ufunc, } -// TODO: Note the entry-point anymore. /* - * The central entry-point for the promotion and dispatching machinery. - * It currently works with the operands (although it would be possible to - * only work with DType (classes/types). + * The main implementation to find the correct DType signature and ArrayMethod + * to use for a ufunc. This function may recurse with `do_legacy_fallback` + * set to False. + * + * If value-based promotion is necessary, this is handled ahead of time by + * `promote_and_get_ufuncimpl`. */ static NPY_INLINE PyObject * promote_and_get_info_and_ufuncimpl(PyUFuncObject *ufunc, @@ -581,10 +584,14 @@ promote_and_get_info_and_ufuncimpl(PyUFuncObject *ufunc, (PyObject **)op_dtypes); if (info != NULL && PyObject_TypeCheck( PyTuple_GET_ITEM(info, 1), &PyArrayMethod_Type)) { + /* Found the ArrayMethod and NOT a promoter: return it */ return info; } - // TODO: Add comment here what this does. + /* + * If `info == NULL`, the caching failed, repeat using the full resolution + * in `resolve_implementation_info`. + */ if (info == NULL) { if (resolve_implementation_info(ufunc, op_dtypes, &info) < 0) { return NULL; @@ -592,8 +599,8 @@ promote_and_get_info_and_ufuncimpl(PyUFuncObject *ufunc, if (info != NULL && PyObject_TypeCheck( PyTuple_GET_ITEM(info, 1), &PyArrayMethod_Type)) { /* - * Cache the new one. NOTE: If we allow a promoter to return - * a new ArrayMethod, we should also cache such a promoter also. + * Found the ArrayMethod and NOT promoter. Before returning it + * add it to the cache for faster lookup in the future. */ if (cache && PyArrayIdentityHash_SetItem(ufunc->_dispatch_cache, (PyObject **)op_dtypes, info, 0) < 0) { @@ -603,6 +610,10 @@ promote_and_get_info_and_ufuncimpl(PyUFuncObject *ufunc, } } + /* + * At this point `info` is NULL if there is no matching loop, or it is + * a promoter that needs to be used/called: + */ if (info != NULL) { PyObject *promoter = PyTuple_GET_ITEM(info, 1); @@ -617,6 +628,7 @@ promote_and_get_info_and_ufuncimpl(PyUFuncObject *ufunc, } /* + * Even using promotion no loop was found. * Using promotion failed, this should normally be an error. * However, we need to give the legacy implementation a chance here. * (it will modify `op_dtypes`). @@ -640,8 +652,10 @@ promote_and_get_info_and_ufuncimpl(PyUFuncObject *ufunc, /* * The central entry-point for the promotion and dispatching machinery. + * * It currently works with the operands (although it would be possible to - * only work with DType (classes/types). + * only work with DType (classes/types). This is because it has to ensure + * that legacy (value-based promotion) is used when necessary. */ NPY_NO_EXPORT PyArrayMethodObject * promote_and_get_ufuncimpl(PyUFuncObject *ufunc, @@ -690,7 +704,7 @@ promote_and_get_ufuncimpl(PyUFuncObject *ufunc, PyArrayMethodObject *method = (PyArrayMethodObject *)PyTuple_GET_ITEM(info, 1); - /* Fill in the signature with the signature that we will be working with */ + /* Fill `signature` with final DTypes used by the ArrayMethod/inner-loop */ PyObject *all_dtypes = PyTuple_GET_ITEM(info, 0); for (int i = 0; i < nargs; i++) { if (signature[i] == NULL) { diff --git a/numpy/core/src/umath/legacy_array_method.c b/numpy/core/src/umath/legacy_array_method.c index 0b60e4fb2..e5043aa71 100644 --- a/numpy/core/src/umath/legacy_array_method.c +++ b/numpy/core/src/umath/legacy_array_method.c @@ -90,15 +90,12 @@ generic_wrapped_legacy_loop(PyArrayMethod_Context *NPY_UNUSED(context), } -// TODO: Rearrange the comment and shorten (probably just to the last line). /* - * This just seems to tricky to make work correctly, there are two main - * problems: First, the resolver really would like the ufunc to be passed in - * (which we could store). Second, the resolver is currently passed the - * full array objects (it is also passed the casting safety instead of - * returning it). Overall, the discrepancy is just a bit much; - * so if this is actually called we are out of luck. - * Otherwise we use it to signal the legacy fallback path in the ufunc code. + * Signal that the old type-resolution function must be used to resolve + * the descriptors (mainly/only used for datetimes due to the unit). + * + * ArrayMethod's are expected to implement this, but it is too tricky + * to support properly. So we simply set an error that should never be seen. */ NPY_NO_EXPORT NPY_CASTING wrapped_legacy_resolve_descriptors(PyArrayMethodObject *NPY_UNUSED(self), |
