Numpy: BUG: np.min nem sempre propaga NaNs

Criado em 11 jan. 2018  ·  65Comentários  ·  Fonte: numpy/numpy

Esta investigação surge porque o scipy CI começou a

No meu computador doméstico (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

No meu computador de trabalho (macOS, conda python 3.6.2, numpy instalado via pip em um ambiente limpo):

>>> 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

Com o primeiro conjunto de exemplos de código, por que os três primeiros exemplos não resultam em um aviso, apenas o último?
Com o segundo conjunto de exemplos, nenhum aviso é emitido.

00 - Bug numpy.core

Todos 65 comentários

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

Acho que outro ponto aqui é que podem ser caminhos de código diferentes que acionam comportamentos diferentes (não muito entorpecidos, eu acho), por exemplo, devido ao uso de AVX ou algo assim, acho que vi às vezes que coisas de vetor falhavam em definir os sinalizadores de CPU de erro, e já que isso pode parecer um pouco aleatório se forem usados ​​ou não ....

Não sei se isso tem muito a ver com isso, mas eu espero, numpy não faz muito sobre erros de ponto flutuante normalmente, eu acho. Exceto verificar se algo aconteceu.

E, como Chuck disse, muitos desses sinalizadores de erro de ponto flutuante são diferentes em sistemas diferentes, infelizmente.

Ok, entendi porque o erro ocorre apenas em algumas instalações e não em outras.
O motivo pelo qual o scipy está usando np.min em um array que possivelmente contém NaN é porque é uma maneira rápida de verificar a presença deles. A documentação numpy sugere que isso é permitido:

Os valores NaN são propagados, ou seja, se pelo menos um item for NaN, o valor mínimo correspondente também será NaN.

Dado que este é um dos casos de uso permitidos para np.min, do ponto de vista do uso, eu não esperaria que nenhum aviso / erro fosse emitido.

(Existem obviamente outras maneiras de conseguir isso, por exemplo, np.isnan(arr).any() , mas isso não muda meu raciocínio acima)

Não é uma mudança plausível, eu acho.

+1 para nenhum aviso

Oi,

Me deparei com uma inconsistência semelhante:

>>> 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 que uma forma de mais de 8 aumenta esse RuntimeWarning?

@NotSqrt @seberg Estou vendo um comportamento semelhante em que min não está propagando NaNs corretamente, quando o tamanho da matriz de entrada chega ao tamanho 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])

Mostra este comportamento bastante estranho no OSX e Windows, mas não no Linux .. fresh virtualenv usando python 2.7.13 e 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

Veja as duas linhas "isto deve ser nan conforme docs: 19,0 "?

Além disso, nb, o aviso não aparece no numpy 1.13.1 (que foi onde observei esse comportamento pela primeira vez).

@kippr Onde você conseguiu o NumPy?

Vou supor que o limite de oito elementos pode estar relacionado ao AVX512 e o problema é alguma combinação de compilador e cpu. Em qual cpus / compilador você está vendo o problema?

@juliantaylor Thoughts?

O alinhamento também pode estar surtindo efeito.

Oi @charris

Obrigado por dar uma olhada no meu post .. para responder às suas perguntas:

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

NumPy foi instalado por meio de pip no meu mac em um novo virtualenv (python 2.7.13 instalado por meio de homebrew), então eu acho que ele usa todos os sinalizadores de compilador padrão etc?

Acabei de recriar a instalação do env virtual e do pip novamente e aqui está o que parece pertinente para mim, mas há muitas mensagens que chegam na instalação .. (log completo anexado). Por favor, me diga se houver outras informações. pode pescar fora do diretório de construção ou outros enfeites, ou se houver opções de compilação para tentar.

[..]
 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

obrigado
Kris

build.log

Ps provavelmente não é surpresa, mas o mesmo comportamento aparece com 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

Estou usando a mesma versão numpy (no arch) com uma CPU que também tem recursos AVX2 e sse4.2 e não vejo isso no Linux. Talvez tenha algo a ver com mac / clang?

EDITAR / Alteração: (eu tentei um pouco provocando alinhamentos diferentes, não verifiquei a parte de aviso, mas a parte do resultado errado eu não vejo)

Estou supondo que há um sinalizador de cpu que não está sendo definido corretamente, provavelmente relacionado ao compilador.

Devíamos ter um teste que detectasse esse problema, nem que fosse para rastreá-lo. @kippr O que faz

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

fazer na sua instalação?

Oi @charris

Isso parece ok:

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])

Mas tentei mais alguns combos e encontrei o seguinte (veja o último, 8 valores e eixo = 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])

Muito misterioso ... não tenho certeza se isso ajuda a entender a causa.

@seberg - sim, isso mesmo, observei esse comportamento em meus macs e também no windows, mas o linux estava bem.

obrigado
Kris

@kippr Este é um bug realmente perturbador, pois afeta uma operação básica e o problema provavelmente não parece ter origem no NumPy. Podemos tentar desligar a vetorização no MAC e no Windows e ver se isso ajuda. Você só vê esse problema no Python 2.7?

Não consigo ver nada no NumPy que teria mudado esse comportamento para 1.14. Talvez o OpenBLAS esteja mexendo nos controles ...

@juliantaylor @VictorRodriguez Pensamentos?

Oi @charris

Também vejo esse comportamento no python 3 no meu 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'

Nb Notei pela primeira vez esse comportamento no NumPy versão 1.13 sob python 2.7 no meu Mac, então não era uma regressão introduzida no 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'

E reproduziu no 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'

Mas não veja este problema no 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 Eu vi em minha jornada sobre otimizações de desempenho para numpy que openblas e sinalizadores de compilador podem afetar o comportamento de numpy / scipy (com base em minha experiência). O primeiro passo que poderíamos dar é testar isso em um teste C (usando bibliotecas openblas) para que possamos isolar o comportamento e ver se ele pode ser replicado. Também só para verificar se isso é apenas no MAC e no Windows, certo? não consigo ver esse comportamento no Linux (fwiw os patches avx entram para 1.15, então eles não devem afetar isso). @kippr como você construiu seu numpy no mac / windows? Saudações

Olá @VictorRodriguez

Isso foi via pip install em ambas as plataformas, um novo virtualenv no caso do Mac. Veja acima onde colei o log do pip, incluindo a saída da compilação para a instalação do python 2.7. (Python 3 pip install parece ser uma roda.)

Saúde Kris

Oi pessoal
Posso fornecer mais alguma informação para ajudar?

obrigado

No mac (e talvez em outras plataformas também), o bug está ocorrendo porque o compilador (no mac é clang / llvm não gcc por padrão) está reordenando os comandos SSE2 de uma maneira problemática. O bug em:

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

está acontecendo porque o código que está sendo executado é o sse2_minimum_DOUBLE gerado a partir de:
https://github.com/numpy/numpy/blob/d7d5cb3feccc1fc6cf57159e8b9fe0a733968706/numpy/core/src/umath/simd.inc.src#L1020

Especificamente, vamos examinar este código (eu expandi automaticamente o formulário SSE2) dentro da função na linha 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);
            ....
         }

O compilador não entende que a atribuição c1 e npy_get_floatstatus () estão relacionados e, portanto, altera o código para:

        }
        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);
            ....
         }

o que é claro que não faz sentido ... Não sei qual é a forma recomendada para não fazer isso nas otimizações. (Eu acho que outras plataformas têm STDC FENV_ACCESS pragma para isso?)

Será que como as mudanças neste código já existem há 5 anos, o bug é causado por uma nova versão do clang e diferentes otimizações?

Obrigado @tzickel

Não escrevo C desde uma aula de segundo grau há muitas luas, então definitivamente não vou descobrir a maneira inteligente de resolver isso, mas tentei algumas coisas para forçar o compilador a avaliar na ordem original alterando a declaração if acima para:

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

E, de fato, assim que faço isso, o bug desaparece.

(Eu também tentei um monte de outras formas menos burras para tentar fazer com que o compilador avaliasse _mm_min_pd antes da instrução if, por exemplo, colocando referências a c1 em ambos os lados da instrução if e após o bloco if / else, tudo para sem sucesso. Suspeito que ele os reordenou de qualquer maneira, mas sempre executou a atribuição c = _mm_min_pd ou percebi que minhas chamadas eram na verdade noops de uma forma ou de outra ..)

Mas, em qualquer caso, posso confirmar que você identificou o bug que estou vendo, vamos esperar que alguém tenha uma boa dica para o compilador deixar a ordem de _mm_min_pd e npy_get_floatstatus sozinha.

Ok, outra coisa que o Google sugeriu que também funcionou: marcar c1 volátil:

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

torna-se:

        /* 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;

Mas não tenho certeza das implicações disso como / se essa é a melhor maneira de atingir o objetivo.

Muito obrigado pela depuração extra neste @tzickel, esta foi uma depuração muito profunda, @kippr Eu tenho uma pergunta, você mencionou que é uma nova instalação do pip em um Mac, então não é para construir do zero, certo? Além disso, se eu executei meu sistema Linux com gcc:

$ python
Python 3.6.5 (padrão, 1º de abril de 2018, 15:40:54)
[GCC 7.3.0] no linux
Digite "ajuda", "copyright", "créditos" ou "licença" para obter mais informações.

importar numpy como np
np.version.version
'1.14.2'
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 = 'aumentar')
{'divide': 'warn', 'over': 'warn', 'under': 'ignore', 'invalid': 'warn'}
np.min ([np.nan, 1,0])
nan

e não vê nenhum aviso, então, conforme descrito, o problema pode ser com o clang. a única coisa que me faz pensar é por que você está vendo isso com pip install numpy. Como foi construído? e quais sinalizadores foram usados?

Também nenhum erro com meu mac:

https://hastebin.com/cuzinajero.swift

Eu me pergunto se isso poderia ser mais seguro com algo como

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

@juliantaylor Thoughts?

Ou mesmo

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

O operador vírgula deve ser um ponto de sequência, embora possa não ser seguro para threads. Hmm, gostaria de saber o quanto no código simd é thread-safe.

@VictorRodriguez se eu executar pip install numpy no meu mac com python2.7, ele cria a partir do código-fonte. Se eu executar pip install com python3, ele será instalado a partir de uma roda.

Capturei o resultado completo da instalação do pip: https://github.com/numpy/numpy/files/1912086/build.log

Para testar as mudanças acima, eu construí a partir da fonte (master atual), mas verifiquei que sem o ajuste eu ainda estava vendo o comportamento quebrado. Ao construir isso, usei a linha de comando sugerida em INSTALL.rst.txt:
python setup.py build -j 4 install --prefix $HOME/.local

Uma palavra de cautela, alguém aqui diz que viu o bug no Windows também. Suponho que não foi compilado com clang no windows, mas com MSVC ou GCC, isso pode significar que o numpy não está respeitando as regras da plataforma. No Windows, numpy usa _statusfp para obter o estado fp, a documentação do MSDN afirma:

Muitas funções de biblioteca matemática modificam a palavra de status de ponto flutuante, com resultados imprevisíveis. A otimização pode reordenar, combinar e eliminar operações de ponto flutuante em torno de chamadas para _status87, _statusfp e funções relacionadas. Use a opção de compilador / Od (Desabilitar (Depurar)) ou a diretiva de pragma fenv_access para evitar otimizações que reordenam as operações de ponto flutuante.

GCC e MSVC (mas sem clang por enquanto) têm um pragma para controlar isso.

@tzickel tenho certeza que o windows build disse que era com cygwin. Não se reproduz para mim com MSVC.

no geral eu gosto da sugestão de @charris , é C padrão, não requer pragmas e AFAICT não adiciona nenhuma 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 é compatível até com o compilador visual studio 8 usado para python 2.7
a segunda sugestão, mais compacta, parece muito ofuscada

Existem outros locais no código que são suscetíveis a esse bug?

  1. O build do cygwin diz GCC, o que significa que o bug deve agir como o Linux, não?

  2. Se eu entendi bem, o operador vírgula não ajuda nas otimizações, ele funciona em outro nível de geração de código. O compilador ainda está livre para pensar que ambas as expressões não estão relacionadas e para movê-las. Aqui está um pequeno exemplo para mostrar que este é o caso (tente alterar o código de comentários com a linha ao lado), verifique o minpd em relação à chamada para a função externa (certifique-se de que -O3 nos sinalizadores do compilador é fornecido) e alterne entre GCC e CLANG (parece que o tronco do GCC tem o mesmo bug por enquanto :)):

https://godbolt.org/g/Zoc5xr

  1. Se você olhar para fenv.h, basicamente todas as funções no código que acessam esse arquivo têm o mesmo potencial de problemas. Portanto, tudo o que está sendo chamado no código está sendo usado em numpy / core / src / npymath / ieee754.c

  2. Na minha opinião, o clang atualmente não pode produzir código seguro para este tipo de funções com otimizações, então as opções são:
    R. Compilar com gcc (pelo menos as rodas mac oficiais) e produzir um aviso se compilado com clang.
    B. O Clang suporta um atributo optnone por função, que pode ser espalhado em tais funções para desativar todas as otimizações (código mais lento, mas correto) se compilado com o clang:
    https://clang.llvm.org/docs/AttributeReference.html#optnone -clang-optnone-clang-optnone

Mudar para o operador vírgula não ajudará em nada - ; já é um ponto de sequência.

@ eric-wieser Tem certeza? Não vejo ; listado como um ponto de sequência. https://msdn.microsoft.com/en-us/library/azk8zbxd.aspx .

Em qualquer caso, a ordem em que os argumentos de um operador vírgula são avaliados é garantida, o que não é o caso com declarações separadas por ; ,

@charris, infelizmente, a mudança sugerida não parece resolver o 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 a variável como volátil resolve o problema.

Avise-me se quiser experimentar outras variantes.

Parece que os compiladores não seguem as especificações relacionadas aos operadores de vírgula, ou pelo menos como entendemos as especificações. A solução mais simples para o estado atual dos compiladores parece ser adicionar volatile , embora pareça frágil.

Bem, o GCC 8.1 acabou de ser lançado e adivinhe ... O GCC com otimizações agora produz o mesmo problema que o clang produz aqui https://github.com/numpy/numpy/issues/10370#issuecomment -384154230 (e a vírgula não ajuda lá como esperado, mas o volátil sim), embora eu não saiba se distutils habilita nos flags do gcc que atenuam isso, eu acho que não.

Aqui está o código no GCC 8.1 (você pode comparar com 7.3 onde está ok):
https://godbolt.org/g/AJRdRQ

Pesquise aqui as chamadas minpd e npy_get_floatstatus no asm.

Por enquanto, vamos com o volátil, juntamente com alguns testes que devem sinalizar um problema, se ele surgir. Outra opção que podemos examinar é uma função separada em que poderíamos usar diretivas voláteis ou de compilador, mas mantê-la em um só lugar para facilitar o gerenciamento.

Este é um caso muito maior do que este problema específico. Todo o código numpy que chama funções em ieee754.c precisa ser auditado e corrigido. (Talvez renomeie este problema mais uma vez, ou melhor ainda, abra um novo problema).

O comportamento surpreendente da instrução minpd pode ser relevante para https://github.com/numpy/numpy/issues/10370#issuecomment -381241813:

Se apenas um valor for NaN (SNaN ou QNaN) para esta instrução, o segundo operando (operando de origem), um NaN ou um valor de ponto flutuante válido, é escrito no resultado.

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

@mattkretz não parece. (esse é o comportamento esperado e por que essa verificação já está em vigor).

@tzickel Por curiosidade, em que nível de otimização o problema aparece? Costumávamos nos limitar a -O2 , mas acho que algumas plataformas têm usado -O3 ou equivalente.

Semelhante a este bug do gcc, eu acho: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=6065

Parece que definir #pragma STDC FENV_ACCESS ON é a solução correta aqui, mas requer C99, que precisamos descartar o python 2.7 para usar. Talvez o modo GCC --std=c90 esteja assumindo que pode fazer a otimização devido ao pragma estar ausente

Para todas as suas perguntas, basta acessar o link aqui:

https://godbolt.org/g/AJRdRQ (* parece compartilhado, então copie a entrada para uma nova janela, talvez)

e tente alterar a versão do compilador, sinalizadores e código do compilador e ver o resultado em tempo real ...

Isso acontece ao usar even -O1 em ambos os compiladores mais recentes. O #pragma parece não fazer nada ....

Essa versão é muito complicada, há muito montador e é difícil relacionar o montador ao código c. A versão original era muito mais simples e também demonstrava o problema.

Veja também o PR

hmm .... alguém pode me dizer em qual SO / compilador foi compilado numpy-1.14.3-cp27-cp27mu-manylinux1_x86_64.whl (do arquivo pypi)? Acho que pode haver outro problema (relacionado) escondido aqui também.

@tzickel Acredito que o compilador ubuntu trusty padrão é o gcc 4.8, não consigo ver se algo mais recente foi instalado.

@charris o contêiner manylinux roda Centos5 com gcc 4.2 instalado.

@ngoldbaum Obrigado pela informação. Precisamos construir rodas para Python 3.7 em breve. Isso será tão fácil quanto adicionar uma entrada MB_PYTHON_VERSION=3.7 à matriz de construção travis.yml?

Acho que você precisará esperar até que as ferramentas do manylinux sejam atualizadas. Isso levou algumas semanas após o lançamento do python3.6, IIRC. @njsmith provavelmente sabe mais.

@ngoldbaum O que gcc 4.2 ? É fácil usar uma versão posterior do compilador, se assim desejarmos?

O que levou à escolha do gcc 4.2?

Acredito que o objetivo era permitir a compilação com uma glibc que seja antiga o suficiente para que os problemas de compatibilidade binária não sejam um problema na prática

É fácil usar uma versão posterior do compilador, se assim desejarmos?

Eu não sei. Eu também não sei como o entorpecido consegue construir rodas. Estou usando o projeto multibuild de Matthew Brett para meus projetos e vou precisar esperar até que seja atualizado para construir rodas python3.7 para meus projetos.

Ok, não tenho certeza se é o problema relacionado final, mas acho que encontrei outro problema nesse código SSE:

No numpy 1.14.0, algum código foi adicionado para lançar os avisos de tempo de execução se houver erros de FP:
https://github.com/numpy/numpy/commit/d47ca7b26172c42b01c3132d0e46e70578c8ea21

Mas se olharmos para a implementação do SSE novamente:
https://github.com/numpy/numpy/blob/d7d5cb3feccc1fc6cf57159e8b9fe0a733968706/numpy/core/src/umath/simd.inc.src#L1020

Podemos ver que ele passa pela FPU apenas a parte alinhada ao meio do array, o cabeçalho e o trailer (que não são SSEable porque não estão alinhados à memória) são verificados manualmente para NaN e não passam pela FPU, enquanto o SSE partes o fazem, portanto, apenas a parte do meio irá disparar um aviso NaN, enquanto a outra (enquanto equivalente na entrada e saída) não. Tudo bem?

np.min ([1, np.nan, 1, 1, 1, 1, 1, 1]) não acionará um aviso de tempo de execução
mas np.min ([1, 1, np.nan, 1, 1, 1, 1, 1]) sim.

np.min ([1, np.nan, 1, 1, 1, 1, 1, 1]) não acionará um aviso de tempo de execução

@tzickel, isso está relacionado ao # 11029, certo?

Editar: formatação

Parece que o problema de origem era # 8954, que levou ao PR # 9020

Sim, mas meu ponto é que o # 9020 não estava cobrindo todos os casos possíveis. Um deles é o código SSE (que subverte esse mecanismo para alguma otimização).

Quanto ao # 11029, estou tentando descobrir por que, tanto nos posts aqui e ali, além do bug de propagação NaN, às vezes aparecem avisos e às vezes não

Outra questão, se NaN é o resultado de ambos min / max se ele existe na entrada, e já verificamos isNan / inválido, ele não deveria sair rapidamente ao descobrir a primeira instância de NaN?

@tzickel nenhuma das operações de redução saída antecipada. Eles podem no futuro se forem refatorados como gufuncs.

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

marcocaccin picture marcocaccin  ·  4Comentários

qualiaa picture qualiaa  ·  3Comentários

thouis picture thouis  ·  4Comentários

inducer picture inducer  ·  3Comentários

keithbriggs picture keithbriggs  ·  3Comentários