Numpy: поведение gufunc при неправильной форме

Созданный на 2 июн. 2020  ·  5Комментарии  ·  Источник: numpy/numpy

Из обсуждения здесь: https://github.com/numpy/numpy/pull/15162#discussion_r434122175

В следующем примере, похоже, не возникает ошибка, когда выходная форма неверна или транслирует входные данные на выход, но молча заполняет выход значениями результат + мусор.

import numpy as np
from numpy.core import _umath_tests as umt
from numpy.testing import assert_raises
a = np.arange(6).reshape(3, 2)
b = np.ones(2)
out = np.empty((5, 3))
umt.inner1d(a, b, out)
print(out)

Результат:

[[ 1.00000000e+000  5.00000000e+000  9.00000000e+000]
 [ 6.91217735e-310 -1.45681599e+144 -1.45681599e+144]
 [-1.45681599e+144 -1.45681599e+144 -1.45681599e+144]
 [-1.45681599e+144 -1.45681599e+144 -1.45681599e+144]
 [-1.45681599e+144 -1.45681599e+144  6.32404027e-322]]
00 - Bug numpy.core numpy.ufunc

Самый полезный комментарий

Ах, это очень просто:

diff --git a/numpy/core/src/umath/ufunc_object.c b/numpy/core/src/umath/ufunc_object.c
index 19876d641..85820e3a0 100644
--- a/numpy/core/src/umath/ufunc_object.c
+++ b/numpy/core/src/umath/ufunc_object.c
@@ -2614,7 +2614,7 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc,
      * dimensions.
      */
     broadcast_ndim = 0;
-    for (i = 0; i < nin; ++i) {
+    for (i = 0; i < nop; ++i) {
         int n = PyArray_NDIM(op[i]) - op_core_num_dims[i];
         if (n > broadcast_ndim) {
             broadcast_ndim = n;

это, наверное, все, что нужно. Тогда мы получаем правильную трансляцию входов в выходы, и любые мысли об отказе от этого можно просто отложить на будущее, если они когда-нибудь возникнут.

РЕДАКТИРОВАТЬ: Плюс нулевая проверка op ...

Все 5 Комментарий

Хе-хе, какое-то время думал об этом… .Также пинг

Флаг NPY_ITER_NO_BROADCAST , как ни странно, кажется, почти всегда используется таким образом, чтобы вы могли просто полностью извлечь этот операнд для обнаружения формы (например, как выходную операцию). Но это было бы несовместимым разрывом, если бы мы просто использовали его для решения этой проблемы.

Поэтому я рассматриваю возможность добавления нового NPY_ITER_OUTPUT_OPERAND . Или создайте NPY_ITER_OUTPUT_OPERAND и другой флаг NPY_ITER_DOES_NOT_AFFECT_SHAPE чтобы флаг выходного операнда мог включать в себя как флаги «выделить», так и «нет трансляции».

Это похоже на комбинацию выделения и без широковещательной передачи, что, вероятно, уже несколько подразумевает. Но вопрос в том, хотим ли мы внести такое изменение, теоретически оно может повлиять на внешние библиотеки, использующие NpyIter. (Если только мы не сделаем это VisibleDeprecationWarning, когда оно сработает, но не уверены, что это очень удобно для реализации.)

Я думаю, это могло бы решить проблему. Есть интересные побочные случаи, такие как оси , которая используется только один (или более) выходных массивов. Можно было бы попытаться поддержать это (в какой-то момент?), Но это, вероятно, добавляет слишком много сложности без реальной выгоды.
Я просто не вижу особых вариантов использования (),()->(k) gufunc. Что-то вроде не (),()->(3,3) или ->(3,3) конечно, имеет смысл, но это не проблема.

Я должен признать, что немного запутался в отношении всех флагов. Но gufunc moments с сигнатурой ()->(k) возможно, мог бы вычислить моменты до k на выходе. Конечно, это невероятно надумано, но я не считаю, что это явно исключать (или код, явно позволяющий это!)

Немного более прямое отношение к проблеме здесь, где выход имеет непоследовательную внешнюю форму, заключается в том, что есть прецедент из обычных ufuncs:

np.add(1, 1, out=np.empty((3,)))
# array([2., 2., 2.])

Это также предполагает, что для gufuncs внешняя форма должна определяться по всем входам и выходам. Очевидно, вызовет ненужные вычисления, но пусть будет так ...

О, я почему-то не ожидал, что это действительно сработает. Я полагаю, что в этом случае мы можем просто позволить выходу вызывать широковещательную передачу на входах (и, таким образом, корректировать форму). В основном просто сохраняя текущее поведение.

так что просто ничего здесь не делайте, кроме как убедиться, что (g) ufunc вызывается несколько раз (это будет немного медленным способом получить результат во многих случаях, но это проблема вызывающих). Хотя я не считаю важным поддерживать это, я также не вижу проблем с этим.

Но, очевидно, нам все еще нужно исправить эту проблему в gufuncs!

Ах, это очень просто:

diff --git a/numpy/core/src/umath/ufunc_object.c b/numpy/core/src/umath/ufunc_object.c
index 19876d641..85820e3a0 100644
--- a/numpy/core/src/umath/ufunc_object.c
+++ b/numpy/core/src/umath/ufunc_object.c
@@ -2614,7 +2614,7 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc,
      * dimensions.
      */
     broadcast_ndim = 0;
-    for (i = 0; i < nin; ++i) {
+    for (i = 0; i < nop; ++i) {
         int n = PyArray_NDIM(op[i]) - op_core_num_dims[i];
         if (n > broadcast_ndim) {
             broadcast_ndim = n;

это, наверное, все, что нужно. Тогда мы получаем правильную трансляцию входов в выходы, и любые мысли об отказе от этого можно просто отложить на будущее, если они когда-нибудь возникнут.

РЕДАКТИРОВАТЬ: Плюс нулевая проверка op ...

Была ли эта страница полезной?
0 / 5 - 0 рейтинги

Смежные вопросы

marcocaccin picture marcocaccin  ·  4Комментарии

astrofrog picture astrofrog  ·  4Комментарии

dcsaba89 picture dcsaba89  ·  3Комментарии

inducer picture inducer  ·  3Комментарии

Foadsf picture Foadsf  ·  3Комментарии