Da discussão aqui: https://github.com/numpy/numpy/pull/15162#discussion_r434122175
O exemplo a seguir não parece gerar um erro quando a forma de saída está incorreta ou transmite as entradas para a saída, mas preenche silenciosamente a saída com resultados + valores lixo.
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)
Resultado:
[[ 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]]
Hehe, estava pensando sobre isso por um tempo… .Também pingue em garantir .
O sinalizador NPY_ITER_NO_BROADCAST
, curiosamente, parece quase sempre usado de uma maneira em que você poderia simplesmente retirar aquele operando totalmente para a descoberta de forma (por exemplo, como uma operação de saída). Mas, seria uma quebra incompatível se apenas o usássemos para resolver esse problema.
Portanto, estou pensando em adicionar um novo NPY_ITER_OUTPUT_OPERAND
. Ou faça um NPY_ITER_OUTPUT_OPERAND
e outro sinalizador NPY_ITER_DOES_NOT_AFFECT_SHAPE
para que o sinalizador do operando de saída possa incluir os sinalizadores "alocar" e "sem transmissão".
Parece uma combinação de alocar e não transmitir, provavelmente já implica isso de alguma forma. Mas a questão é se queremos fazer essa mudança, em teoria, ela pode afetar bibliotecas externas usando o NpyIter. (A menos que o tornemos um VisibleDeprecationWarning quando ele entrar em ação, mas não temos certeza de que seja muito legal de implementar)
Acho que isso pode resolver o problema. Existem casos colaterais interessantes, como eixos que são usados apenas por um (ou mais) vetores de saída. Alguém poderia tentar oferecer suporte a isso (em algum momento?), Mas provavelmente adiciona muita complexidade para nenhum ganho real.
Eu simplesmente não vejo muito caso de uso para (),()->(k)
gufunc. Algo como (),()->(3,3)
ou ->(3,3)
faz sentido, é claro, mas não é problema.
Um pouco confuso com todas as bandeiras, devo admitir. Mas um gufunc moments
com assinatura ()->(k)
talvez pudesse calcular momentos até k
na saída. Claro, incrivelmente rebuscado, mas meu sentido seria não excluí-lo explicitamente (nem codificar explicitamente para permiti-lo!).
Um pouco mais diretamente relevante para o problema aqui, onde a saída tem uma forma externa inconsistente, é que há precedente dos ufuncs regulares:
np.add(1, 1, out=np.empty((3,)))
# array([2., 2., 2.])
Isso sugere também para gufuncs, a forma externa deve ser determinada a partir de todas as entradas e saídas. Obviamente, causará cálculos desnecessários, mas que assim seja ...
Oh, de alguma forma eu não esperava que isso realmente funcionasse. Suponho que, nesse caso, podemos simplesmente permitir que a saída cause a transmissão nas entradas (e, assim, ajustar a forma). Basicamente, simplesmente mantendo o comportamento atual.
portanto, não faça nada aqui, exceto certificar-se de que (g) ufunc seja chamado várias vezes (será um modo um pouco lento de obter o resultado em muitos casos, mas esse é o problema dos chamadores). Embora eu não ache importante apoiar isso, também não vejo problemas em fazê-lo.
Mas ainda precisamos corrigir esse problema no gufuncs, obviamente!
Ah, é muito simples:
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;
é provavelmente tudo que há para fazer. Em seguida, obtemos a transmissão correta de entradas para saídas, e qualquer pensamento de descontinuar isso pode ser adiado para as futuras selves, se isso ocorrer.
EDIT: Além da verificação nula de op ...
Comentários muito úteis
Ah, é muito simples:
é provavelmente tudo que há para fazer. Em seguida, obtemos a transmissão correta de entradas para saídas, e qualquer pensamento de descontinuar isso pode ser adiado para as futuras selves, se isso ocorrer.
EDIT: Além da verificação nula de op ...