Numpy: ERROR: np.min no siempre propaga NaN

Creado en 11 ene. 2018  ·  65Comentarios  ·  Fuente: numpy/numpy

Esta investigación surge de que scipy CI comenzó a fallar en appveyor en los últimos días, es decir, después de que se lanzó 1.14.

En la computadora de mi casa (macOS, conda python 3.6.2, conda numpy):

>>> import numpy as np
>>> np.version.version
'1.14.0'
>>> np.min([1.0, 2.0, np.nan])
nan
>>> np.min([1.0, np.nan, 2.0])
nan
>>> np.min([np.nan, 1.0, 2.0])
nan
>>> np.min([np.nan, 1.0])
/Users/andrew/miniconda3/envs/dev3/lib/python3.6/site-packages/numpy/core/_methods.py:29: RuntimeWarning: invalid value encountered in reduce
  return umr_minimum(a, axis, None, out, keepdims)
nan

En mi computadora de trabajo (macOS, conda python 3.6.2, numpy instalado a través de pip en un entorno limpio):

>>> import numpy as np
>>> np.version.version
'1.14.0'
>>> np.min([1., 2., 3., 4., np.nan])
nan
>>> np.min([1., 2., 3., np.nan, 4.])
nan
>>> np.min([1., 2., np.nan, 3., 4.])
nan
>>> np.min([1., np.nan, 2., 3., 4.])
nan
>>> np.min([np.nan, 1., 2., 3., 4.])
nan
>>> np.min([np.nan, 1.])
nan
>>> np.min([np.nan, 1., np.nan])
nan
>>> np.min([1., np.nan])
nan
>>> np.seterr(all='raise')
{'divide': 'warn', 'over': 'warn', 'under': 'ignore', 'invalid': 'warn'}
>>> np.min([1., np.nan])
nan
>>> np.min([np.nan, 1.])
nan
>>> np.min([np.nan, 1., 2., 3., 4.])
nan
>>> np.min([np.nan, 1., 2., 3., 4.])
nan

Con el primer conjunto de ejemplos de código, ¿por qué los primeros tres ejemplos no dan como resultado una advertencia, solo el último?
Con el segundo conjunto de ejemplos no se emite ninguna advertencia.

00 - Bug numpy.core

Todos 65 comentarios

xref scipy / scipy # 8282, scipy / scipy # 8279

Creo que otro punto aquí es que podrían ser diferentes rutas de código que desencadenan un comportamiento diferente (creo que no realmente numpy), por ejemplo, debido al uso de AVX o algo así, creo que a veces vi que el material vectorial no pudo establecer los indicadores de error de la CPU, y ya que eso puede parecer un poco aleatorio si se usan o no ...

No sé si eso tiene mucho que ver con eso, pero lo espero, creo que numpy no hace demasiado con los errores de punto flotante. Excepto comprobar si pasó algo.

Y como dijo Chuck, gran parte de esta bandera de error de punto flotante es diferente en diferentes sistemas, desafortunadamente.

Bien, entendido por qué el error solo ocurre en algunas instalaciones y no en otras.
La razón por la que scipy está usando np.min en una matriz que posiblemente contiene NaN es porque es una forma rápida de verificar la presencia de ellos. La documentación numpy sugiere que esto está permitido:

Los valores de NaN se propagan, es decir, si al menos un elemento es NaN, el valor mínimo correspondiente también será NaN.

Dado que este es uno de los casos de uso permitidos para np.min, desde un punto de vista de uso, no esperaría que se emitieran advertencias / errores.

(Obviamente, hay otras formas de lograr esto, por ejemplo, np.isnan(arr).any() , pero eso no cambia mi razonamiento anterior)

Supongo que no es un cambio plausible.

+1 para no recibir advertencias

Hola,

Me encontré con una inconsistencia similar:

>>> import numpy
>>> import warnings
>>> warnings.simplefilter("always", category=RuntimeWarning)
>>> numpy.min(numpy.full((7,), numpy.nan, dtype=numpy.float64))
nan
>>> numpy.min(numpy.full((8,), numpy.nan, dtype=numpy.float64))
/home/user/env/lib/python3.6/site-packages/numpy/core/_methods.py:29: RuntimeWarning: invalid value encountered in reduce
  return umr_minimum(a, axis, None, out, keepdims)
nan

¿Por qué una forma de más de 8 aumenta ese RuntimeWarning?

@NotSqrt @seberg Estoy viendo un comportamiento similar en el que min no propaga los NaN correctamente en absoluto, cuando el tamaño de la matriz de entrada llega al tamaño 8:

> cat np-min-wat.py
import numpy as np

print "numpy version:", np.__version__
print ""

def show_min(input):
    print ""
    arr = np.array(input)
    print arr.dtype, arr
    print "this should be nan as per docs:", arr.min()
    arr = np.array

input = [31., 487., np.nan, 10000., 10000., 19., 101., 22., 1000., 300., 10.]
for x in xrange(3, len(input) + 1):
    show_min(input[0:x])

Muestra este comportamiento bastante extraño en OSX y Windows, pero no en Linux .. fresh virtualenv usando python 2.7.13 y numpy 1.14.2:

numpy version: 1.14.2

/Users/kip/ac/Environments/test/lib/python2.7/site-packages/numpy/core/_methods.py:29: RuntimeWarning: invalid value encountered in reduce
  return umr_minimum(a, axis, None, out, keepdims)

float64 [ 31. 487.  nan]
this should be nan as per docs: nan

float64 [   31.   487.    nan 10000.]
this should be nan as per docs: nan

float64 [   31.   487.    nan 10000. 10000.]
this should be nan as per docs: nan

float64 [   31.   487.    nan 10000. 10000.    19.]
this should be nan as per docs: nan

float64 [   31.   487.    nan 10000. 10000.    19.   101.]
this should be nan as per docs: nan

float64 [   31.   487.    nan 10000. 10000.    19.   101.    22.]
this should be nan as per docs: 19.0

float64 [   31.   487.    nan 10000. 10000.    19.   101.    22.  1000.]
this should be nan as per docs: 19.0

float64 [   31.   487.    nan 10000. 10000.    19.   101.    22.  1000.   300.]
this should be nan as per docs: nan

float64 [   31.   487.    nan 10000. 10000.    19.   101.    22.  1000.   300.
    10.]
this should be nan as per docs: nan

¿Ves las dos líneas "esto debería ser nan según documentos: 19.0 "?

Además, la advertencia no aparece en numpy 1.13.1 (que es donde observé este comportamiento por primera vez).

@kippr ¿ sacaste NumPy?

Supongo que el límite de ocho elementos puede estar relacionado con AVX512 y el problema es una combinación de compilador y CPU. ¿En qué cpus / compilador está viendo el problema?

@juliantaylor ¿Pensamientos?

La alineación también podría tener un efecto.

Hola @charris

Gracias por echarle un vistazo a mi publicación ... para responder a tus preguntas:

CPU :
❃ sysctl -n machdep.cpu.brand_string Intel (R) Core (TM) i7-6700K CPU a 4,00 GHz

NumPy se instaló a través de pip en mi mac en un nuevo virtualenv (python 2.7.13 instalado a través de homebrew), así que supongo que usa todas las banderas del compilador predeterminadas, etc.

Acabo de recrear el entorno virtual y volver a ejecutar la instalación de pip, y esto es lo que me parece pertinente, pero hay muchos mensajes que aparecen en la instalación ... (registro completo adjunto). Por favor, avíseme si hay otra información que puede pescar fuera del directorio de compilación o lo que sea, o si hay banderas de compilación para probar.

[..]
 Downloading numpy-1.14.2.zip (4.9MB):                                                                                                                                                                                                                                            
  Downloading from URL https://pypi.python.org/packages/0b/66/86185402ee2d55865c675c06a5cfef742e39f4635a4ce1b1aefd20711c13/numpy-1.14.2.zip#md5=080f01a19707cf467393e426382c7619 (from https://pypi.python.org/simple/numpy/)                                                      
...Downloading numpy-1.14.2.zip (4.9MB): 4.9MB downloaded              
[..]
    building library "npymath" sources
    get_default_fcompiler: matching types: '['gnu95', 'nag', 'absoft', 'ibm', 'intel', 'gnu', 'g95', 'pg']'
    customize Gnu95FCompiler
    Found executable /usr/local/bin/gfortran
    customize Gnu95FCompiler
    customize Gnu95FCompiler using config
    C compiler: clang -fno-strict-aliasing -fno-common -dynamic -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/System/Library/Frameworks/Tk.framework/Versions/8.5/Headers -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes

    compile options: '-Inumpy/core/src/private -Inumpy/core/src -Inumpy/core -Inumpy/core/src/npymath -Inumpy/core/src/multiarray -Inumpy/core/src/umath -Inumpy/core/src/npysort -I/usr/local/Cellar/python/2.7.13/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'
    clang: _configtest.c
    clang _configtest.o -o _configtest
    success!
    removing: _configtest.c _configtest.o _configtest

Gracias
Kris

build.log

Ps probablemente no sea una sorpresa, pero el mismo comportamiento aparece con max:

numpy version: 1.14.2


arr.dtype, arr: float64 [ 31. 487.  nan]
/Users/kip/ac/Environments/meh/lib/python2.7/site-packages/numpy/core/_methods.py:29: RuntimeWarning: invalid value encountered in reduce
  return umr_minimum(a, axis, None, out, keepdims)
arr.min(): nan
/Users/kip/ac/Environments/meh/lib/python2.7/site-packages/numpy/core/_methods.py:26: RuntimeWarning: invalid value encountered in reduce
  return umr_maximum(a, axis, None, out, keepdims)
arr.max(): nan
np.amin(arr): nan
np.nanmin(arr): 31.0

arr.dtype, arr: float64 [   31.   487.    nan 10000.]
arr.min(): nan
arr.max(): nan
np.amin(arr): nan
np.nanmin(arr): 31.0

arr.dtype, arr: float64 [   31.   487.    nan 10000. 10000.]
arr.min(): nan
arr.max(): nan
np.amin(arr): nan
np.nanmin(arr): 31.0

arr.dtype, arr: float64 [   31.   487.    nan 10000. 10000.    19.]
arr.min(): nan
arr.max(): nan
np.amin(arr): nan
np.nanmin(arr): 19.0

arr.dtype, arr: float64 [   31.   487.    nan 10000. 10000.    19.   101.]
arr.min(): nan
arr.max(): nan
np.amin(arr): nan
np.nanmin(arr): 19.0

arr.dtype, arr: float64 [   31.   487.    nan 10000. 10000.    19.   101.    22.]
arr.min(): 19.0
arr.max(): 10000.0
np.amin(arr): 19.0
np.nanmin(arr): 19.0

arr.dtype, arr: float64 [   31.   487.    nan 10000. 10000.    19.   101.    22.  1000.]
arr.min(): 19.0
arr.max(): 10000.0
np.amin(arr): 19.0
np.nanmin(arr): 19.0

arr.dtype, arr: float64 [   31.   487.    nan 10000. 10000.    19.   101.    22.  1000.   300.]
arr.min(): nan
arr.max(): nan
np.amin(arr): nan
np.nanmin(arr): 19.0

arr.dtype, arr: float64 [   31.   487.    nan 10000. 10000.    19.   101.    22.  1000.   300.
    10.]
arr.min(): nan
arr.max(): nan
np.amin(arr): nan
np.nanmin(arr): 10.0

Estoy usando la misma versión numpy (en arch) con una CPU que también tiene capacidades AVX2 y sse4.2 y no la veo en linux. ¿Quizás tenga algo que ver con mac / clang?

EDITAR / Enmienda: (Probé un poco para provocar diferentes alineaciones, no verifiqué la parte de advertencia, pero no veo la parte de resultado incorrecta)

Supongo que hay un indicador de CPU que no se está configurando correctamente, probablemente relacionado con el compilador.

Deberíamos tener una prueba que revele este problema, aunque solo sea para rastrearlo. @kippr ¿Qué hace

np.min(np.diagflat([np.nan]*10), axis=0)

hacer en su instalación?

Hola @charris

Eso parece estar bien:

In [1]: import numpy as np

In [2]: np.min(np.diagflat([np.nan]*10), axis=0)
Out[2]: array([ nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan])

Pero probé algunos combos más y encontré lo siguiente (ver el último, 8 valores y eje = 1):

In [3]: np.min(np.diagflat([np.nan]*10), axis=1)
Out[3]: array([ nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan])

In [4]: np.min(np.diagflat([np.nan]*8), axis=0)
Out[4]: array([ nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan])

In [5]: np.min(np.diagflat([np.nan]*8), axis=1)
Out[5]: array([ nan,  nan,   0.,   0.,  nan,   0.,  nan,  nan])

Bastante misterioso ... no estoy seguro si eso ayuda a entender la causa en absoluto.

@seberg : sí, es cierto, observé este comportamiento en mi Mac y también en Windows, pero Linux estaba bien.

Gracias
Kris

@kippr Este es un error realmente perturbador, ya que afecta una operación básica y el problema probablemente no parece tener su origen en NumPy. Podemos intentar desactivar la vectorización en MAC y Windows y ver si eso ayuda. ¿Solo ve este problema en Python 2.7?

No puedo ver nada en NumPy que haya cambiado este comportamiento para 1.14. Quizás OpenBLAS esté jugando con los controles ...

@juliantaylor @VictorRodriguez ¿Pensamientos?

Hola @charris

Veo este comportamiento también en Python 3 en mi Mac:

Python 3.6.0 (default, Jan 23 2017, 20:09:28)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy as np

>>> np.min(np.diagflat([np.nan]*8), axis=1)
array([nan, nan,  0.,  0., nan,  0., nan, nan])
>>> np.__version__
'1.14.2'

Nota: noté este comportamiento por primera vez en la versión 1.13 de NumPy bajo python 2.7 en mi Mac, por lo que no fue una regresión introducida en 1.14:

Python 2.7.10 (default, Feb  7 2017, 00:08:15)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.34)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy as np
>>> np.min(np.diagflat([np.nan]*8), axis=1)
array([ nan,  nan,   0.,   0.,  nan,   0.,  nan,  nan])
>>> np.__version__
'1.13.1'

Y lo reproducí en cygwin / windows:

$ python
Python 2.7.13 (default, Mar 13 2017, 20:56:15)
[GCC 5.4.0] on cygwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy as np
>>> np.min(np.diagflat([np.nan]*8), axis=1)
array([ nan,  nan,   0.,   0.,  nan,   0.,  nan,  nan])
>>> np.__version__
'1.13.1'

Pero no vea este problema en Linux:

Python 2.7.6 (default, Oct 26 2016, 20:32:47) 
[GCC 4.8.4] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy as np
>>> np.min(np.diagflat([np.nan]*8), axis=1)
array([ nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan])
>>> np.__version__
'1.13.1'

@charris He visto en mi viaje sobre optimizaciones de rendimiento para numpy que openblas y los indicadores del compilador pueden afectar el comportamiento de numpy / scipy (según mi experiencia). El primer paso que podríamos tomar es agregar esto a una prueba C (usando bibliotecas openblas) para que podamos aislar el comportamiento y ver si se puede replicar. También para verificar esto es solo en MAC y Windows, ¿verdad? No puedo ver este comportamiento en Linux (ahora los parches avx entran en 1.15, por lo que no deberían afectar esto). @kippr ¿cómo construiste tu numpy en mac / windows? Saludos

Hola @VictorRodriguez

Esto fue a través de la instalación de pip en ambas plataformas, un nuevo virtualenv en el caso de Mac. Vea arriba donde pegué el registro para pip, incluida la salida de compilación para la instalación de Python 2.7. (La instalación de Python 3 pip parece ser una rueda).

Saludos Kris

Hola amigos
¿Algo más que pueda proporcionar para ayudar?

Gracias

En mac (y quizás también en otras plataformas) el error se produce porque el compilador (en mac es clang / llvm no gcc por defecto) está reordenando los comandos SSE2 de una manera problemática. El error en:

np.min(np.diagflat([np.nan]*8), axis=1)

está sucediendo porque el código que se está ejecutando es el sse2_minimum_DOUBLE generado a partir de:
https://github.com/numpy/numpy/blob/d7d5cb3feccc1fc6cf57159e8b9fe0a733968706/numpy/core/src/umath/simd.inc.src#L1020

Específicamente, veamos este código (expandí automáticamente el formulario SSE2) dentro de la función en la línea 1041:

        }
        c1 = _mm_min_pd(c1, c2);

        if (npy_get_floatstatus() & NPY_FPE_INVALID) {
            *op = NPY_NAN;
        }
        else {
            npy_double tmp = sse2_horizontal_min___m128d(c1);
            ....
         }

El compilador no comprende que la asignación c1 y npy_get_floatstatus () están relacionados y, por lo tanto, cambia el código a:

        }
        if (npy_get_floatstatus() & NPY_FPE_INVALID) {
            *op = NPY_NAN;
        }
        else {
            c1 = _mm_min_pd(c1, c2);
            npy_double tmp = sse2_horizontal_min___m128d(c1);
            ....
         }

lo cual, por supuesto, no tiene sentido ... No sé cuál es la forma recomendada de hacer que no haga esto bajo optimizaciones. (¿Creo que otras plataformas tienen pragma STDC FENV_ACCESS para esto?)

¿Quizás dado que los cambios en este código tienen 5 años, el error se debe a una nueva versión de clang y diferentes optimizaciones?

Gracias @tzickel

No he escrito C desde una clase de secundaria hace muchas lunas, así que definitivamente no voy a encontrar la forma inteligente de resolver esto, pero intenté algunas cosas para obligar al compilador a evaluar en el orden original cambiando la declaración if de lo anterior a:

if ((sizeof(c1) != 0) || (sizeof(c1) == 0) & npy_get_floatstatus() & NPY_FPE_INVALID) {

Y de hecho, una vez que hago esto, el error desaparece.

(También probé un montón de otras formas menos tontas para intentar que el compilador evalúe _mm_min_pd antes de la declaración if, por ejemplo, poniendo referencias a c1 en ambos lados de la declaración if y después del bloque if / else, todo para en vano. Sospecho que los reordenó de todos modos pero siempre ejecutó la asignación c = _mm_min_pd, o pensé que mis llamadas eran en realidad noops de una forma u otra ...)

Pero en cualquier caso, puedo confirmar que ha identificado el error que estoy viendo, esperemos que alguien tenga una buena manera de insinuar al compilador que deje el orden de _mm_min_pd y npy_get_floatstatus solo.

Ok, otra cosa que Google sugirió que también funcionó: marcar c1 como volátil:

https://github.com/numpy/numpy/blob/d7d5cb3feccc1fc6cf57159e8b9fe0a733968706/numpy/core/src/umath/simd.inc.src#L1029

se convierte en:

        /* load the first elements */
        @vtype@ volatile c1 = @vpre@_load_@vsuf@((@type@*)&ip[i]);
        @vtype@ c2 = @vpre@_load_@vsuf@((@type@*)&ip[i + stride]);
        i += 2 * stride;

Pero no estoy seguro de qué implicaciones tiene o si esa es la mejor manera de lograr el objetivo.

Muchas gracias por la depuración adicional en este @tzickel, esta fue una depuración muy profunda, @kippr Tengo una pregunta, mencionas que es una nueva instalación de pip en una Mac, por lo que no se supone que se compile desde cero, ¿verdad? Además, si ejecuté mi sistema Linux con gcc:

$ python
Python 3.6.5 (predeterminado, 1 de abril de 2018, 15:40:54)
[GCC 7.3.0] en Linux
Escriba "ayuda", "derechos de autor", "créditos" o "licencia" para obtener más información.

importar numpy como np
np.version.version
"1.14.2"
np.min ([1., 2., 3., 4., np.nan])
yaya
np.min ([1., 2., 3., np.nan, 4.])
yaya
np.min ([1., 2., np.nan, 3., 4.])
yaya
np.min ([1., np.nan, 2., 3., 4.])
yaya
np.min ([np.nan, 1., 2., 3., 4.])
yaya
np.min ([np.nan, 1.])
yaya
np.min ([np.nan, 1., np.nan])
yaya
np.min ([1., np.nan])
yaya
np.seterr (todos = 'subir')
{'dividir': 'advertir', 'sobre': 'advertir', 'debajo': 'ignorar', 'inválido': 'advertir'}
np.min ([np.nan, 1,0])
yaya

y no ve ninguna advertencia, por lo que tal vez, tal como se describe, el problema podría ser el sonido metálico. lo único que me hace preguntarme es por qué estás viendo esto con pip install numpy. ¿Cómo fue construido? y que banderas se usaron?

Tampoco hay error con mi mac:

https://hastebin.com/cuzinajero.swift

Me pregunto si esto podría hacerse más seguro con algo como

if (c1 = n_mm_min_pd(c1, c2), py_get_floatstatus() & NPY_FPE_INVALID) {

@juliantaylor ¿Pensamientos?

O incluso

return_nan = (c1 = n_mm_min_pd(c1, c2), py_get_floatstatus() & NPY_FPE_INVALID);
if (return_nan) {

Se supone que el operador de coma es un punto de secuencia, aunque puede que no sea seguro para subprocesos. Hmm, me pregunto cuánto en el código simd es seguro para subprocesos.

@VictorRodriguez si ejecuto pip install numpy en mi mac con python2.7, se construye desde la fuente. Si ejecuto pip install con python3, se instala desde una rueda.

Capturé el resultado completo de la instalación de pip: https://github.com/numpy/numpy/files/1912086/build.log

Para probar los cambios anteriores, construí desde la fuente (maestro actual), pero verifiqué que sin el ajuste todavía estaba viendo el comportamiento roto. Al construir eso, utilicé la línea de comando sugerida en INSTALL.rst.txt:
python setup.py build -j 4 install --prefix $HOME/.local

Una advertencia, alguien aquí dice que también ha visto el error en las ventanas. Supongo que no se compiló con clang en Windows, pero con MSVC o GCC, esto podría significar que numpy no está respetando las reglas de la plataforma. En Windows, numpy usa _statusfp para obtener el estado de fp, la documentación de MSDN dice:

Muchas funciones de la biblioteca matemática modifican la palabra de estado de punto flotante, con resultados impredecibles. La optimización puede reordenar, combinar y eliminar operaciones de punto flotante alrededor de llamadas a _status87, _statusfp y funciones relacionadas. Utilice la opción del compilador / Od (Desactivar (Depurar)) o la directiva pragma fenv_access para evitar optimizaciones que reordenan las operaciones de punto flotante.

GCC y MSVC (pero no clang por ahora) tienen un pragma para controlar esto.

@tzickel estaba bastante seguro de que la compilación de Windows decía que era con cygwin. No se reproduce para mí con MSVC.

en general, me gusta la sugerencia de @charris , es estándar C, no requiere pragmas y AFAICT no agrega ninguna sobrecarga

/* use the comma operator to prevent optimization changing the order of evaluation */
if (c1 = n_mm_min_pd(c1, c2), py_get_floatstatus() & NPY_FPE_INVALID) {

que es compatible incluso con el compilador de Visual Studio 8 utilizado para Python 2.7
la segunda sugerencia, más compacta, parece demasiado confusa

¿Hay otros lugares en el código que sean susceptibles a este error?

  1. La compilación cygwin dice GCC, lo que significa que el error debería actuar como linux, ¿no?

  2. Si lo entiendo correctamente, el operador de coma no ayuda con las optimizaciones, funciona en otro nivel de la generación de código. El compilador todavía es libre de pensar que ambas expresiones no están relacionadas y de moverlas. Aquí hay un pequeño ejemplo para mostrar que este es el caso (intente cambiar el código de comentarios con la línea al lado), verifique el minpd relativo a la llamada a la función externa (asegúrese de que se proporcione -O3 en los indicadores del compilador) y cambie entre GCC y CLANG (parece que el tronco de GCC tiene el mismo error por ahora :)):

https://godbolt.org/g/Zoc5xr

  1. Si observa fenv.h, básicamente todas las funciones del código que acceden a ese archivo tienen el mismo potencial de problemas. Entonces, todo lo que se llama en el código desde ese se usa en numpy / core / src / npymath / ieee754.c

  2. En mi opinión, clang actualmente no puede producir código seguro para este tipo de funciones con optimizaciones, por lo que las opciones son:
    A. Compile con gcc (al menos las ruedas oficiales de mac) y genere una advertencia si se compila con clang.
    B. Clang admite un atributo optnone por función, que se puede agregar a dichas funciones para deshabilitar todas las optimizaciones (código más lento pero correcto) si se compila con clang:
    https://clang.llvm.org/docs/AttributeReference.html#optnone -clang-optnone-clang-optnone

Cambiar al operador de coma no ayudará en absoluto: ; ya es un punto de secuencia.

@ eric-wieser ¿Estás seguro? No veo ; listado como un punto de secuencia. https://msdn.microsoft.com/en-us/library/azk8zbxd.aspx .

En cualquier caso, se garantiza el orden en el que se evalúan los argumentos de un operador de coma, que no es el caso con declaraciones separadas por ; ,

@charris, lamentablemente, el cambio sugerido no parece resolver el problema:

diff --git a/numpy/core/src/umath/simd.inc.src b/numpy/core/src/umath/simd.inc.src
index 2241414ac..8345e3ef7 100644
--- a/numpy/core/src/umath/simd.inc.src
+++ b/numpy/core/src/umath/simd.inc.src
@@ -1038,9 +1038,8 @@ sse2_@kind@_@TYPE@(@type@ * ip, @type@ * op, const npy_intp n)
             c1 = @vpre@_@VOP@_@vsuf@(c1, v1);
             c2 = @vpre@_@VOP@_@vsuf@(c2, v2);
         }
-        c1 = @vpre@_@VOP@_@vsuf@(c1, c2);

-        if (npy_get_floatstatus() & NPY_FPE_INVALID) {
+        if (c1 = @vpre@_@VOP@_@vsuf@(c1, c2), npy_get_floatstatus() & NPY_FPE_INVALID) {
             *op = @nan@;
         }
         else {

NB: marcar la variable como volátil soluciona el problema.

Avísame si quieres probar otras variantes.

Parece que los compiladores no se adhieren a la especificación con respecto a los operadores de coma, o al menos como entendemos la especificación. La solución más simple para el estado actual de los compiladores parece ser agregar volatile , aunque parece frágil.

Bueno, GCC 8.1 se acaba de lanzar y adivina qué ... GCC con optimizaciones ahora produce el mismo problema que el clang hace aquí https://github.com/numpy/numpy/issues/10370#issuecomment -384154230 (y la coma no ayudar allí como se esperaba, pero el volátil sí), aunque no sé si distutils habilita en las banderas gcc que mitigan esto, creo que no.

Aquí está el código en GCC 8.1 (puede comparar con 7.3 donde está bien):
https://godbolt.org/g/AJRdRQ

Busque allí la llamada minpd y npy_get_floatstatus en el asm.

Vayamos con volátiles por el momento junto con algunas pruebas que, con suerte, deberían indicar un problema si surge. Otra opción que podríamos considerar es una función separada donde podríamos usar directivas volátiles o de compilación, pero tenerla en un solo lugar para que sea más fácil de administrar.

Este es un caso mucho más importante que este problema específico. Todo el código numpy que llama a funciones en ieee754.c necesita ser auditado y arreglado. (Tal vez cambie el nombre de este problema una vez más, o mejor aún, abra uno nuevo).

El comportamiento sorprendente de la instrucción minpd puede ser relevante para https://github.com/numpy/numpy/issues/10370#issuecomment -381241813:

Si solo un valor es un NaN (SNaN o QNaN) para esta instrucción, el segundo operando (operando fuente), ya sea un NaN o un valor de punto flotante válido, se escribe en el resultado.

Ver: http://www.felixcloutier.com/x86/MINPD.html

@mattkretz no lo parece. (ese es el comportamiento esperado y por qué esta verificación ya está en su lugar).

@tzickel Por curiosidad, ¿en qué nivel de optimización aparece el problema? Solíamos limitarnos a -O2 , pero creo que algunas plataformas han estado usando -O3 o equivalente.

Similar a este error de gcc, creo: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=6065

Parece que configurar #pragma STDC FENV_ACCESS ON es la solución correcta aquí, pero requiere C99, para lo cual necesitamos eliminar Python 2.7 para usarlo. Quizás el modo GCC --std=c90 asume que puede realizar la optimización debido a que el pragma está ausente

Para todas sus preguntas, vaya al enlace aquí:

https://godbolt.org/g/AJRdRQ (* parece compartido, así que copie la entrada en una nueva ventana tal vez)

e intente cambiar la versión del compilador, los indicadores y el código del compilador, y vea el resultado sobre la marcha ...

Esto sucede cuando se usa incluso -O1 en los dos últimos compiladores. El #pragma no parece hacer nada ....

Esa versión es muy complicada, hay demasiado ensamblador y es difícil relacionar el ensamblador con el código c. La versión original era mucho más simple y también demostraba el problema.

Ver también el PR

hmm ... ¿alguien puede decirme en qué sistema operativo / compilador se compiló numpy-1.14.3-cp27-cp27mu-manylinux1_x86_64.whl (del archivo pypi)? Creo que podría haber otro problema (relacionado) escondido aquí también.

@tzickel Creo que el compilador de confianza de ubuntu predeterminado es gcc 4.8, no puedo ver que se haya instalado algo más reciente.

@charris el contenedor manylinux ejecuta Centos5 con gcc 4.2 instalado.

@ngoldbaum Gracias por la información. Pronto tendremos que construir ruedas para Python 3.7. ¿Será tan fácil como agregar una entrada MB_PYTHON_VERSION=3.7 a la matriz de compilación travis.yml?

Creo que tendrá que esperar hasta que se actualicen las herramientas de manylinux. Eso tomó algunas semanas después de que Python3.6 saliera IIRC. @njsmith probablemente sepa más.

@ngoldbaum ¿Qué gcc 4.2 ? ¿Es fácil utilizar una versión posterior del compilador si así lo deseamos?

¿Qué motivó la elección de gcc 4.2?

Creo que el objetivo era habilitar la compilación con un glibc que sea lo suficientemente antiguo como para que los problemas con la compatibilidad binaria no sean un problema en la práctica.

¿Es fácil utilizar una versión posterior del compilador si así lo deseamos?

No lo sé. Tampoco sé cómo maneja Numpy la construcción de ruedas. Estoy usando el proyecto de construcción múltiple de Matthew Brett para mis proyectos y tendré que esperar hasta que esté actualizado para construir ruedas de python3.7 para mis proyectos.

Ok, no estoy seguro si es el último problema relacionado, pero creo que encontré otro problema en ese código SSE:

En numpy 1.14.0 se agregó un código para lanzar las advertencias de tiempo de ejecución si hay errores de FP:
https://github.com/numpy/numpy/commit/d47ca7b26172c42b01c3132d0e46e70578c8ea21

Pero si miramos la implementación de SSE nuevamente:
https://github.com/numpy/numpy/blob/d7d5cb3feccc1fc6cf57159e8b9fe0a733968706/numpy/core/src/umath/simd.inc.src#L1020

Podemos ver que pasa a través de la FPU solo la parte alineada en el medio de la matriz, el encabezado y el tráiler (que no son SSEable porque no están alineados con la memoria) se verifican manualmente para NaN y no pasan a través de la FPU, mientras que SSE las partes sí lo hacen, por lo que solo la parte del medio activará una advertencia de NaN, mientras que la otra (aunque sea equivalente en la entrada y salida) no lo hará. Eso esta bien ?

np.min ([1, np.nan, 1, 1, 1, 1, 1, 1]) no activará una advertencia de tiempo de ejecución
pero np.min ([1, 1, np.nan, 1, 1, 1, 1, 1]) sí lo hace.

np.min ([1, np.nan, 1, 1, 1, 1, 1, 1]) no activará una advertencia de tiempo de ejecución

@tzickel esto está relacionado con # 11029, ¿verdad?

Editar: formateo

Parece que el problema de origen fue el # 8954, que llevó al PR # 9020

Sí, pero lo que quiero decir es que el número 9020 no cubría todos los casos posibles. Uno de ellos es este código SSE (que subvierte este mecanismo para alguna optimización).

En cuanto al n. ° 11029, estoy tratando de averiguar incluso por qué tanto en publicaciones aquí como allá, además del error de propagación de NaN, a veces aparecen advertencias y otras no

Otra pregunta, si NaN es el resultado de ambos min / max si existe en la entrada, y ya comprobamos isNan / invalid, ¿no debería salir rápidamente al descubrir la primera instancia de NaN?

@tzickel ninguna de las operaciones de reducción de salida anticipada. Podrían hacerlo en el futuro si se refactorizan como gufuncs.

¿Fue útil esta página
0 / 5 - 0 calificaciones