@@ -267,8 +267,39 @@ resolve_implementation_info(PyUFuncObject *ufunc,
267267 * the subclass should be considered a better match
268268 * (subclasses are always more specific).
269269 */
270+ /* Whether this (normally output) dtype was specified at all */
271+ if (op_dtypes [i ] == NULL ) {
272+ /*
273+ * When DType is completely unspecified, prefer abstract
274+ * over concrete, assuming it will resolve.
275+ * Furthermore, we cannot decide which abstract/None
276+ * is "better", only concrete ones which are subclasses
277+ * of Abstract ones are defined as worse.
278+ */
279+ npy_bool prev_is_concrete = NPY_FALSE ;
280+ npy_bool new_is_concrete = NPY_FALSE ;
281+ if ((prev_dtype != Py_None ) &&
282+ !NPY_DT_is_abstract ((PyArray_DTypeMeta * )prev_dtype )) {
283+ prev_is_concrete = NPY_TRUE ;
284+ }
285+ if ((new_dtype != Py_None ) &&
286+ !NPY_DT_is_abstract ((PyArray_DTypeMeta * )new_dtype )) {
287+ new_is_concrete = NPY_TRUE ;
288+ }
289+ if (prev_is_concrete == new_is_concrete ) {
290+ best = -1 ;
291+ }
292+ else if (prev_is_concrete ) {
293+ unambiguously_equally_good = 0 ;
294+ best = 1 ;
295+ }
296+ else {
297+ unambiguously_equally_good = 0 ;
298+ best = 0 ;
299+ }
300+ }
270301 /* If either is None, the other is strictly more specific */
271- if (prev_dtype == Py_None ) {
302+ else if (prev_dtype == Py_None ) {
272303 unambiguously_equally_good = 0 ;
273304 best = 1 ;
274305 }
@@ -289,13 +320,29 @@ resolve_implementation_info(PyUFuncObject *ufunc,
289320 */
290321 best = -1 ;
291322 }
323+ else if (!NPY_DT_is_abstract ((PyArray_DTypeMeta * )prev_dtype )) {
324+ /* old is not abstract, so better (both not possible) */
325+ unambiguously_equally_good = 0 ;
326+ best = 0 ;
327+ }
328+ else if (!NPY_DT_is_abstract ((PyArray_DTypeMeta * )new_dtype )) {
329+ /* new is not abstract, so better (both not possible) */
330+ unambiguously_equally_good = 0 ;
331+ best = 1 ;
332+ }
292333 /*
293- * TODO: Unreachable, but we will need logic for abstract
294- * DTypes to decide if one is a subclass of the other
295- * (And their subclass relation is well defined.)
334+ * TODO: This will need logic for abstract DTypes to decide if
335+ * one is a subclass of the other (And their subclass
336+ * relation is well defined). For now, we bail out
337+ * in cas someone manages to get here.
296338 */
297339 else {
298- assert (0 );
340+ PyErr_SetString (PyExc_NotImplementedError ,
341+ "deciding which one of two abstract dtypes is "
342+ "a better match is not yet implemented. This "
343+ "will pick the better (or bail) in the future." );
344+ * out_info = NULL ;
345+ return -1 ;
299346 }
300347
301348 if ((current_best != -1 ) && (current_best != best )) {
@@ -612,6 +659,35 @@ promote_and_get_info_and_ufuncimpl(PyUFuncObject *ufunc,
612659 }
613660 return info ;
614661 }
662+ else if (info == NULL && op_dtypes [0 ] == NULL ) {
663+ /*
664+ * If we have a reduction, fill in the unspecified input/array
665+ * assuming it should have the same dtype as the operand input
666+ * (or the output one if given).
667+ * Then, try again. In some cases, this will choose different
668+ * paths, such as `ll->?` instead of an `??->?` loop for `np.equal`
669+ * when the input is `.l->.` (`.` meaning undefined). This will
670+ * then cause an error. But cast to `?` would always lose
671+ * information, and in many cases important information:
672+ *
673+ * ```python
674+ * from operator import eq
675+ * from functools import reduce
676+ *
677+ * reduce(eq, [1, 2, 3]) != reduce(eq, [True, True, True])
678+ * ```
679+ *
680+ * The special cases being `logical_(and|or|xor)` which can always
681+ * cast to boolean ahead of time and still give the right answer
682+ * (unsafe cast to bool is fine here). We special case these at
683+ * the time of this comment (NumPy 1.21).
684+ */
685+ assert (ufunc -> nin == 2 && ufunc -> nout == 1 );
686+ op_dtypes [0 ] = op_dtypes [2 ] != NULL ? op_dtypes [2 ] : op_dtypes [1 ];
687+ Py_INCREF (op_dtypes [0 ]);
688+ return promote_and_get_info_and_ufuncimpl (ufunc ,
689+ ops , signature , op_dtypes , allow_legacy_promotion , 1 );
690+ }
615691 }
616692
617693 /*
@@ -743,3 +819,94 @@ promote_and_get_ufuncimpl(PyUFuncObject *ufunc,
743819
744820 return method ;
745821}
822+
823+
824+ /*
825+ * Special promoter for the logical ufuncs. The logical ufuncs can always
826+ * use the ??->? and still get the correct output (as long as the output
827+ * is not supposed to be `object`).
828+ */
829+ static int
830+ logical_ufunc_promoter (PyUFuncObject * NPY_UNUSED (ufunc ),
831+ PyArray_DTypeMeta * op_dtypes [], PyArray_DTypeMeta * signature [],
832+ PyArray_DTypeMeta * new_op_dtypes [])
833+ {
834+ /*
835+ * If we find any object DType at all, we currently force to object.
836+ * However, if the output is specified and not object, there is no point,
837+ * it should be just as well to cast the input rather than doing the
838+ * unsafe out cast.
839+ */
840+ int force_object = 0 ;
841+
842+ for (int i = 0 ; i < 3 ; i ++ ) {
843+ PyArray_DTypeMeta * item ;
844+ if (signature [i ] != NULL ) {
845+ item = signature [i ];
846+ Py_INCREF (item );
847+ if (item -> type_num == NPY_OBJECT ) {
848+ force_object = 1 ;
849+ }
850+ }
851+ else {
852+ /* Always override to boolean */
853+ item = PyArray_DTypeFromTypeNum (NPY_BOOL );
854+ if (op_dtypes [i ] != NULL && op_dtypes [i ]-> type_num == NPY_OBJECT ) {
855+ force_object = 1 ;
856+ }
857+ }
858+ new_op_dtypes [i ] = item ;
859+ }
860+
861+ if (!force_object || (op_dtypes [2 ] != NULL
862+ && op_dtypes [2 ]-> type_num != NPY_OBJECT )) {
863+ return 0 ;
864+ }
865+ /*
866+ * Actually, we have to use the OBJECT loop after all, set all we can
867+ * to object (that might not work out, but try).
868+ *
869+ * NOTE: Change this to check for `op_dtypes[0] == NULL` to STOP
870+ * returning `object` for `np.logical_and.reduce(obj_arr)`
871+ * which will also affect `np.all` and `np.any`!
872+ */
873+ for (int i = 0 ; i < 3 ; i ++ ) {
874+ if (signature [i ] != NULL ) {
875+ continue ;
876+ }
877+ Py_SETREF (new_op_dtypes [i ], PyArray_DTypeFromTypeNum (NPY_OBJECT ));
878+ }
879+ return 0 ;
880+ }
881+
882+
883+ NPY_NO_EXPORT int
884+ install_logical_ufunc_promoter (PyObject * ufunc )
885+ {
886+ if (PyObject_Type (ufunc ) != (PyObject * )& PyUFunc_Type ) {
887+ PyErr_SetString (PyExc_RuntimeError ,
888+ "internal numpy array, logical ufunc was not a ufunc?!" );
889+ return -1 ;
890+ }
891+ PyObject * dtype_tuple = PyTuple_Pack (3 ,
892+ & PyArrayDescr_Type , & PyArrayDescr_Type , & PyArrayDescr_Type , NULL );
893+ if (dtype_tuple == NULL ) {
894+ return -1 ;
895+ }
896+ PyObject * promoter = PyCapsule_New (& logical_ufunc_promoter ,
897+ "numpy._ufunc_promoter" , NULL );
898+ if (promoter == NULL ) {
899+ Py_DECREF (dtype_tuple );
900+ return -1 ;
901+ }
902+
903+ PyObject * info = PyTuple_Pack (2 , dtype_tuple , promoter );
904+ Py_DECREF (dtype_tuple );
905+ Py_DECREF (promoter );
906+ if (info == NULL ) {
907+ return -1 ;
908+ }
909+
910+ return PyUFunc_AddLoop ((PyUFuncObject * )ufunc , info , 0 );
911+ }
912+
0 commit comments