De la discusión aquí: https://github.com/numpy/numpy/pull/15162#discussion_r434122175
El siguiente ejemplo no parece generar un error cuando nuestra forma es incorrecta o transmite las entradas a la salida, pero llena silenciosamente la salida con valores de resultado + basura.
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]]
Jeje, estuve pensando en esto por un tiempo… .También haz ping a
La bandera NPY_ITER_NO_BROADCAST
, por extraño que parezca, parece que casi siempre se usa de una manera en la que podría simplemente sacar ese operando completamente para el descubrimiento de formas (por ejemplo, como una operación de salida). Pero sería una ruptura incompatible si solo lo usáramos para abordar ese problema.
Así que estoy considerando agregar un nuevo NPY_ITER_OUTPUT_OPERAND
. O haga un NPY_ITER_OUTPUT_OPERAND
y otro indicador NPY_ITER_DOES_NOT_AFFECT_SHAPE
para que el indicador del operando de salida pueda incluir tanto el indicador "asignar" como el indicador "no difusión".
Se siente como una combinación de asignación y no transmisión, probablemente ya lo implique de alguna manera. Pero la pregunta es si queremos hacer tal cambio, en teoría puede afectar a bibliotecas externas usando NpyIter. (A menos que lo hagamos un VisibleDeprecationWarning cuando se active, pero no estoy seguro de que sea muy fácil de implementar).
Creo que eso podría solucionar el problema. Hay casos secundarios interesantes, como un eje que solo es utilizado por una (o más) matrices de salida. Se podría intentar respaldar eso (¿en algún momento?), Pero probablemente agrega demasiada complejidad para no obtener una ganancia real.
Simplemente no veo mucho de un caso de uso para un (),()->(k)
gufunc. Algo como no (),()->(3,3)
o ->(3,3)
tiene sentido, por supuesto, pero no es un problema.
Un poco confundido acerca de todas las banderas, debo admitir. Pero un gufunc moments
con la firma ()->(k)
quizás podría calcular momentos hasta k
en la salida. Por supuesto, increíblemente inverosímil, pero mi sentido sería no excluirlo explícitamente (¡ni codificar explícitamente para permitirlo!).
Un poco más directamente relevante para el problema aquí, donde la salida tiene una forma externa inconsistente, es que hay un precedente de los ufuncs regulares:
np.add(1, 1, out=np.empty((3,)))
# array([2., 2., 2.])
Esto sugiere que también para gufuncs, la forma exterior debe determinarse a partir de todas las entradas y salidas. Obviamente, causará cálculos innecesarios, pero que así sea ...
Oh, de alguna manera no esperaba que eso realmente funcionara. Supongo que en ese caso podemos simplemente permitir que la salida provoque la transmisión en las entradas (y así ajustar la forma). Básicamente simplemente conservando el comportamiento actual.
por lo tanto, no haga nada aquí, excepto asegurarse de que se llame a (g) ufunc varias veces (será una forma un poco lenta de obtener el resultado en muchos casos, pero ese es el problema de las personas que llaman). Si bien no creo que sea importante apoyar esto, tampoco veo ningún problema en hacerlo.
¡Pero todavía tenemos que solucionar este problema en gufuncs obviamente!
Ah, es muy simple:
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;
es probablemente todo lo que hay que hacer. Luego, obtenemos una transmisión correcta de las entradas a las salidas, y cualquier pensamiento de desaprobar esto puede ser pospuesto para nosotros mismos en el futuro si alguna vez surge.
EDITAR: Además de la verificación nula de op ...
Comentario más útil
Ah, es muy simple:
es probablemente todo lo que hay que hacer. Luego, obtenemos una transmisión correcta de las entradas a las salidas, y cualquier pensamiento de desaprobar esto puede ser pospuesto para nosotros mismos en el futuro si alguna vez surge.
EDITAR: Además de la verificación nula de op ...