Numpy: BUG: np.min ne propage pas toujours les NaN

Créé le 11 janv. 2018  ·  65Commentaires  ·  Source: numpy/numpy

Cette enquête provient du fait que scipy CI a commencé à échouer sur appveyor ces derniers jours, c'est-à-dire après la sortie de la version 1.14.

Sur mon ordinateur personnel (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

Sur mon ordinateur de travail (macOS, conda python 3.6.2, numpy installé via pip dans un environnement propre):

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

Avec le premier ensemble d'exemples de code, pourquoi les trois premiers exemples ne génèrent-ils pas un avertissement, seulement le dernier?
Avec le deuxième ensemble d'exemples, aucun avertissement n'est émis du tout.

00 - Bug numpy.core

Tous les 65 commentaires

xréf scipy / scipy # 8282, scipy / scipy # 8279

Je pense qu'un autre point ici est qu'il peut s'agir de chemins de code différents qui déclenchent un comportement différent (pas vraiment numpy je pense), par exemple en raison de l'utilisation d'AVX ou autre, je pense avoir vu parfois que des éléments vectoriels échouaient à définir les indicateurs d'erreur du processeur, et car cela peut paraître un peu aléatoire s'ils sont utilisés ou non ....

Je ne sais pas si cela a beaucoup à voir avec ça, mais je m'y attendais, numpy ne fait pas trop pour les erreurs en virgule flottante normalement je pense. Sauf vérifier si quelque chose s'est passé.

Et comme Chuck l'a dit, une grande partie de cet indicateur d'erreur en virgule flottante est malheureusement différente sur différents systèmes.

Ok, compris pourquoi l'erreur ne se produit que sur certaines installations et pas sur d'autres.
La raison pour laquelle scipy utilise np.min sur un tableau contenant éventuellement NaN est que c'est un moyen rapide de vérifier leur présence. La documentation numpy suggère que cela est permis:

Les valeurs NaN sont propagées, c'est-à-dire que si au moins un élément est NaN, la valeur minimale correspondante sera également NaN.

Étant donné que c'est l'un des cas d'utilisation autorisés pour np.min, du point de vue de l'utilisation, je ne m'attendrais pas à ce que des avertissements / erreurs soient émis du tout.

(Il existe évidemment d'autres moyens d'y parvenir, par exemple np.isnan(arr).any() , mais cela ne change pas mon raisonnement ci-dessus)

Ce n'est pas du tout un changement plausible, je suppose.

+1 pour aucun avertissement

Salut,

Je suis tombé sur une incohérence similaire:

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

Pourquoi une forme de plus de 8 soulève ce RuntimeWarning?

@NotSqrt @seberg Je constate un comportement similaire où min ne propage pas du tout correctement les NaN, lorsque la taille du tableau d'entrée atteint la taille 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])

Montre ce comportement plutôt étrange sur OSX et Windows, mais pas Linux .. virtualenv frais en utilisant python 2.7.13 et 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

Voir les deux lignes "cela devrait être nan selon docs: 19,0 "?

De plus, l'avertissement n'apparaît pas sur numpy 1.13.1 (c'est là que j'ai observé ce comportement pour la première fois.)

@kippr Où avez-vous obtenu NumPy?

Je vais deviner que la limite des huit éléments peut être liée à AVX512 et que le problème est une combinaison de compilateur et de processeur. Sur quel processeur / compilateur voyez-vous le problème?

@juliantaylor Pensées?

L'alignement peut également avoir un effet.

Salut @charris

Merci d'avoir jeté un œil à mon post .. pour répondre à vos questions:

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

NumPy a été installé via pip sur mon mac dans un nouveau virtualenv (python 2.7.13 installé via homebrew), donc je suppose qu'il utilise tous les indicateurs de compilateur par défaut, etc.

Je viens de recréer l'environnement virtuel et réanalyser l'installation de pip, et voici ce qui me semble pertinent, mais il y a beaucoup de messages qui arrivent lors de l'installation .. (journal complet joint.) S'il vous plaît laissez-moi savoir s'il y a d'autres informations I peut pêcher dans le répertoire de construction ou quoi que ce soit, ou s'il y a des indicateurs de compilation à essayer.

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

Merci
Kris

build.log

Ps probablement pas de surprise, mais le même comportement apparaît avec 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

J'utilise la même version numpy (sur arch) avec un processeur qui a également des capacités AVX2 et sse4.2 et ne le vois pas sur Linux. Peut-être que cela a quelque chose à voir avec mac / clang?

EDIT / Ammendment: (j'avais essayé un peu de provoquer différents alignements, je n'ai pas vérifié la partie d'avertissement, mais la partie de résultat erronée que je ne vois pas)

Je suppose qu'il y a un indicateur de processeur qui n'est pas défini correctement, probablement lié au compilateur.

Nous devrions avoir un test qui révèle ce problème, ne serait-ce que pour le suivre. @kippr Que fait

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

faire sur votre installation?

Salut @charris

Cela semble correct:

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

Mais j'ai essayé quelques autres combos et j'ai trouvé ce qui suit (voir le dernier, 8 valeurs et axe = 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])

Assez mystérieux ... je ne sais pas si cela aide à comprendre la cause du tout.

@seberg - oui c'est vrai, j'ai observé ce comportement sur mes macs et aussi sur Windows, mais Linux était bien.

Merci
Kris

@kippr C'est un bogue vraiment dérangeant, car il affecte une opération de base et le problème ne semble probablement pas provenir de NumPy. Nous pouvons essayer de désactiver la vectorisation sur MAC et Windows et voir si cela aide. Ne voyez-vous ce problème que sur Python 2.7?

Je ne vois rien dans NumPy qui aurait changé ce comportement pour la version 1.14. Peut-être qu'OpenBLAS tripote les commandes ...

@juliantaylor @VictorRodriguez Pensées?

Salut @charris

Je vois ce comportement également en python 3 sur mon 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 j'ai d'abord remarqué ce comportement dans NumPy version 1.13 sous python 2.7 sur mon Mac, donc ce n'était pas une régression introduite 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'

Et l'a reproduit dans 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'

Mais ne voyez pas ce problème sous 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 J'ai vu au cours de mon voyage sur l'optimisation des performances pour numpy que openblas et les indicateurs de compilateur peuvent affecter le comportement de numpy / scipy (basé sur mon expérience). La première étape que nous pourrions faire est de coller ceci sur un test C (en utilisant des bibliothèques openblas) afin que nous puissions isoler le comportement et voir s'il peut être répliqué. Aussi juste pour vérifier que c'est juste sur MAC et Windows, n'est-ce pas? Je ne peux pas voir ce comportement sur Linux (fwiw les correctifs avx entrent pour 1.15, ils ne devraient donc pas affecter cela). @kippr comment avez-vous construit votre numpy sous mac / windows? Cordialement

Salut @VictorRodriguez

C'était via pip install sur les deux plates-formes, une nouvelle virtualenv dans le cas Mac. Voir ci-dessus où j'ai collé le journal de pip, y compris la sortie de compilation pour l'installation de python 2.7. (L'installation de Python 3 pip semble être une roue.)

Vive Kris

Salut les gens
Y a-t-il plus d'informations que je peux fournir pour vous aider?

Merci

Sur le mac (et peut-être sur d'autres plates-formes également), le bogue se produit parce que le compilateur (sur mac, c'est clang / llvm pas gcc par défaut) réorganise les commandes SSE2 de manière problématique. Le bug dans:

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

se produit parce que le code en cours d'exécution est le sse2_minimum_DOUBLE généré à partir de:
https://github.com/numpy/numpy/blob/d7d5cb3feccc1fc6cf57159e8b9fe0a733968706/numpy/core/src/umath/simd.inc.src#L1020

Regardons plus précisément ce code (j'ai automatiquement développé le formulaire SSE2) à l'intérieur de la fonction à la ligne 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);
            ....
         }

Le compilateur ne comprend pas que l'affectation c1 et npy_get_floatstatus () sont liés, et change donc le code en:

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

ce qui est bien sûr insensé ... Je ne sais pas quelle est la manière recommandée de ne pas le faire sous optimisations. (Je pense que d'autres plates-formes ont le pragma STDC FENV_ACCESS pour cela?)

Peut-être que puisque les changements dans ce code datent de 5 ans, le bug est-il causé par une nouvelle version de clang et différentes optimisations?

Merci @tzickel

Je n'ai pas écrit C depuis une classe de lycée il y a plusieurs lunes, donc je ne vais certainement pas trouver la façon intelligente de résoudre cela, mais j'ai essayé quelques choses pour forcer le compilateur à évaluer dans l'ordre d'origine en changer l'instruction if de ce qui précède en:

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

Et en effet, une fois que je fais cela, le bogue disparaît.

(J'ai également essayé un tas d'autres formes moins stupides pour essayer d'amener le compilateur à évaluer _mm_min_pd avant l'instruction if, par exemple en mettant des références à c1 des deux côtés de l'instruction if et après le bloc if / else, tout à aucun résultat. Je soupçonne qu'il les a réorganisés de toute façon mais a toujours exécuté l'affectation c = _mm_min_pd, ou a pensé que mes appels étaient en fait noops d'une forme ou d'une autre ..)

Mais dans tous les cas, je peux confirmer que vous avez identifié le bogue que je vois, espérons que quelqu'un a un bon moyen d'indiquer au compilateur de laisser l'ordre de _mm_min_pd et npy_get_floatstatus seul.

Ok, une autre chose que google a suggérée a également fonctionné: marquer c1 volatile:

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

devient:

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

Mais je ne suis pas sûr de ce que cela implique / si c'est la meilleure façon d'atteindre l'objectif.

Merci beaucoup pour le débogage supplémentaire sur ce @tzickel c'était un débogage très profond, @kippr J'ai une question, vous mentionnez qu'il s'agit d'une nouvelle installation de pip sur un Mac, donc n'est pas censé construire à partir de zéro, n'est-ce pas? Aussi si j'ai couru sur mon système Linux avec gcc:

$ python
Python 3.6.5 (par défaut, 1 avril 2018, 15:40:54)
[GCC 7.3.0] sous Linux
Tapez "aide", "copyright", "crédits" ou "licence" pour plus d'informations.

importer numpy comme 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 (tout = 'augmenter')
{'divide': 'warn', 'over': 'warn', 'under': 'ignore', 'invalid': 'warn'}
np.min ([np.nan, 1.0])
nan

et ne voyez aucun avertissement du tout, alors peut-être que le problème pourrait être avec clang. la seule chose qui me fait me demander est pourquoi vous voyez cela avec pip install numpy. Comment a-t-il été construit? et quels drapeaux ont été utilisés?

Aussi aucune erreur avec mon mac:

https://hastebin.com/cuzinajero.swift

Je me demande si cela pourrait être rendu plus sûr avec quelque chose comme

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

@juliantaylor Pensées?

Ou même

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

L'opérateur virgule est censé être un point de séquence, bien qu'il puisse ne pas être thread-safe. Hmm, je me demande à quel point le code simd est thread-safe.

@VictorRodriguez si

J'ai capturé la sortie complète de l'installation de pip: https://github.com/numpy/numpy/files/1912086/build.log

Pour tester les changements ci-dessus, j'ai construit à partir de la source (maître actuel), mais j'ai vérifié que sans le réglage, je voyais toujours le comportement cassé. Lors de la construction, j'ai utilisé la ligne de commande suggérée dans INSTALL.rst.txt:
python setup.py build -j 4 install --prefix $HOME/.local

Un mot d'avertissement, quelqu'un ici dit qu'il a également vu le bogue dans Windows. Je suppose qu'il n'a pas été compilé avec clang sur Windows, mais avec MSVC ou GCC, cela pourrait signifier que numpy ne respecte pas les règles de la plate-forme. Dans Windows, numpy utilise _statusfp pour obtenir l'état fp, la documentation MSDN déclare:

De nombreuses fonctions de la bibliothèque mathématique modifient le mot d'état à virgule flottante, avec des résultats imprévisibles. L'optimisation peut réorganiser, combiner et éliminer les opérations en virgule flottante autour des appels à _status87, _statusfp et aux fonctions associées. Utilisez l'option du compilateur / Od (Disable (Debug)) ou la directive pragma fenv_access pour empêcher les optimisations qui réorganisent les opérations en virgule flottante.

GCC et MSVC (mais pas clang pour l'instant) ont un pragma pour contrôler cela.

@tzickel à peu près sûr que la construction de Windows a dit que c'était avec cygwin. Cela ne se reproduit pas pour moi avec MSVC.

dans l'ensemble, j'aime la suggestion de @charris , c'est du C standard, ne nécessite aucun pragmas, et AFAICT n'ajoute aucun surcoût

/* 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) {

qui est pris en charge même par le compilateur visual studio 8 utilisé pour python 2.7
la deuxième suggestion, plus compacte, semble trop obscure

Y a-t-il d'autres endroits dans le code qui sont sensibles à ce bogue?

  1. La compilation cygwin dit GCC, ce qui signifie que le bogue devrait agir comme linux non?

  2. Si je comprends bien, l'opérateur virgule n'aide pas avec les optimisations, il fonctionne à un autre niveau de la génération de code. Le compilateur est toujours libre de penser que les deux expressions ne sont pas liées et de les déplacer. Voici un petit exemple pour montrer que c'est le cas (essayez de changer le code des commentaires avec la ligne à côté), vérifiez le minpd relatif à l'appel à la fonction extérieure (assurez-vous que -O3 dans les indicateurs du compilateur est donné), et basculez entre GCC et CLANG (il semble que le tronc GCC ait le même bogue pour l'instant :)):

https://godbolt.org/g/Zoc5xr

  1. Si vous regardez fenv.h, pratiquement toutes les fonctions du code qui accèdent à ce fichier ont le même potentiel de problèmes. Donc, tout ce qui est appelé dans le code à partir de cela est utilisé dans numpy / core / src / npymath / ieee754.c

  2. À mon avis, clang ne peut actuellement pas produire de code sûr pour ce type de fonctions avec des optimisations, les options sont donc:
    A. Compilez avec gcc (au moins les roues mac officielles) et produisez un avertissement si compilé avec clang.
    B.Clang prend en charge un attribut optnone par fonction, qui peut être saupoudré sur ces fonctions pour désactiver toutes les optimisations (code plus lent mais correct) si compilé avec clang:
    https://clang.llvm.org/docs/AttributeReference.html#optnone -clang-optnone-clang-optnone

Passer à l'opérateur virgule n'aidera pas du tout - ; est déjà un point de séquence.

@ eric-wieser Êtes-vous sûr? Je ne vois pas ; comme point de séquence. https://msdn.microsoft.com/en-us/library/azk8zbxd.aspx .

Dans tous les cas, l'ordre dans lequel les arguments d'un opérateur virgule sont évalués est garanti, ce qui n'est pas le cas avec des instructions séparées par ; ,

@charris, malheureusement, le changement suggéré ne semble pas résoudre le problème:

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: marquer la variable comme volatile résout le problème.

Faites-moi savoir si vous souhaitez essayer d'autres variantes.

Il semble que les compilateurs n'adhèrent pas à la spécification concernant les opérateurs de virgule, ou du moins telle que nous la comprenons. La solution la plus simple pour l'état actuel des compilateurs semble être d'ajouter volatile , même si cela semble fragile.

Eh bien, GCC 8.1 vient de sortir et devinez quoi ... GCC avec des optimisations produit maintenant le même problème que clang fait ici https://github.com/numpy/numpy/issues/10370#issuecomment -384154230 (et la virgule ne le fait pas aider comme prévu, mais le volatile le fait), bien que je ne sache pas si distutils permet dans les drapeaux gcc qui atténuent cela, je pense que non.

Voici le code sur GCC 8.1 (vous pouvez comparer à 7.3 là où c'est ok):
https://godbolt.org/g/AJRdRQ

Recherchez ici l'appel minpd et npy_get_floatstatus dans l'asm.

Allons-y avec volatile pour le moment avec quelques tests qui devraient, espérons-le, signaler un problème si cela se produit. Une autre option que nous pourrions examiner est une fonction distincte où nous pourrions utiliser des directives volatiles ou du compilateur, mais l'avoir en un seul endroit pour faciliter la gestion.

C'est un cas beaucoup plus important que ce problème spécifique. Tout le code numpy qui appelle des fonctions dans ieee754.c doit être vérifié et corrigé. (Peut-être renommer ce problème encore une fois, ou mieux encore, ouvrir un nouveau numéro).

Le comportement surprenant de l'instruction minpd peut être pertinent pour https://github.com/numpy/numpy/issues/10370#issuecomment -381241813:

Si une seule valeur est un NaN (SNaN ou QNaN) pour cette instruction, le deuxième opérande (opérande source), soit un NaN ou une valeur à virgule flottante valide, est écrit dans le résultat.

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

@mattkretz ne semble pas être le cas. (c'est le comportement attendu et pourquoi cette vérification est déjà en place).

@tzickel Par curiosité, à quel niveau d'optimisation apparaît le problème? Nous avions l'habitude de nous limiter à -O2 , mais je pense que certaines plates-formes utilisaient -O3 ou équivalent.

Semblable à ce bogue gcc, je pense: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=6065

Il semble que définir #pragma STDC FENV_ACCESS ON est la bonne solution ici, mais cela nécessite C99, que nous devons abandonner python 2.7 pour l'utiliser. Peut-être que le mode GCC --std=c90 suppose qu'il peut effectuer l'optimisation en raison de l'absence du pragma

Pour toutes vos questions, rendez-vous sur le lien ici:

https://godbolt.org/g/AJRdRQ (* cela semble partagé, alors copiez peut-être l'entrée dans une nouvelle fenêtre)

et essayez de changer la version du compilateur, les indicateurs et le code du compilateur, et de voir le résultat à la volée ...

Cela se produit lorsque vous utilisez même -O1 sur les deux derniers compilateurs. Le #pragma ne semble rien faire ...

Cette version est très compliquée, il y a trop d'assembleur et il est difficile de relier l'assembleur au code c. La version originale était beaucoup plus simple et démontrait également le problème.

Voir aussi le PR

hmm .... quelqu'un peut-il me dire sur quel système d'exploitation / compilateur a été compilé numpy-1.14.3-cp27-cp27mu-manylinux1_x86_64.whl (de l'archive pypi)? Je pense qu'il pourrait y avoir un autre problème (lié) qui se cache ici également.

@tzickel Je crois que le compilateur de confiance ubuntu par défaut est gcc 4.8, je ne vois pas que quelque chose de plus récent a été installé.

@charris le conteneur manylinux exécute Centos5 avec gcc 4.2 installé.

@ngoldbaum Merci pour l'info. Nous devrons bientôt construire des roues pour Python 3.7. Est-ce que ce sera aussi simple que d'ajouter une entrée MB_PYTHON_VERSION=3.7 à la matrice de construction travis.yml?

Je pense que vous devrez attendre que les outils manylinux soient mis à jour. Cela a pris quelques semaines après la sortie de python3.6 IIRC. @njsmith en sait probablement plus.

@ngoldbaum Qu'est-ce qui a gcc 4.2 ? Est-il facile d'utiliser une version ultérieure du compilateur si nous le souhaitons?

Qu'est-ce qui a motivé le choix de gcc 4.2?

Je pense que l'objectif était de permettre la compilation avec une glibc suffisamment ancienne pour que les problèmes de compatibilité binaire ne soient pas un problème en pratique

Est-il facile d'utiliser une version ultérieure du compilateur si nous le souhaitons?

Je ne sais pas. Je ne sais pas non plus comment numpy gère les roues de construction. J'utilise le projet multibuild de Matthew Brett pour mes projets et je devrai attendre qu'il soit mis à jour pour construire des roues python3.7 pour mes projets.

Ok, je ne sais pas si c'est le dernier problème lié, mais je pense avoir trouvé un autre problème dans ce code SSE:

Dans numpy 1.14.0, du code a été ajouté pour lancer les avertissements d'exécution s'il y a des erreurs FP:
https://github.com/numpy/numpy/commit/d47ca7b26172c42b01c3132d0e46e70578c8ea21

Mais si nous regardons à nouveau l'implémentation SSE:
https://github.com/numpy/numpy/blob/d7d5cb3feccc1fc6cf57159e8b9fe0a733968706/numpy/core/src/umath/simd.inc.src#L1020

Nous pouvons voir qu'il ne traverse le FPU que la partie alignée au milieu du tableau, l'en-tête et la fin (qui ne sont pas SSE car ils ne sont pas alignés en mémoire) sont vérifiés manuellement pour NaN et ne sont pas passés via le FPU, tandis que le SSE les parties le font, donc seule la partie centrale déclenchera un avertissement NaN, tandis que l'autre (bien qu'équivalent dans l'entrée et la sortie) ne le fera pas. Est-ce que ça va?

np.min ([1, np.nan, 1, 1, 1, 1, 1, 1]) ne déclenchera pas d'avertissement d'exécution
mais np.min ([1, 1, np.nan, 1, 1, 1, 1, 1]) le fait.

np.min ([1, np.nan, 1, 1, 1, 1, 1, 1]) ne déclenchera pas d'avertissement d'exécution

@tzickel c'est lié à # 11029, non?

Modifier: formatage

Il semble que le problème source était le n ° 8954, ce qui a conduit au PR n ° 9020

Oui, mais ce que je veux dire, c'est que # 9020 ne couvrait pas tous les cas possibles. L'un d'eux est ce code SSE (qui subvertit ce mécanisme pour une certaine optimisation).

En ce qui concerne # 11029, j'essaie de comprendre même pourquoi dans les messages ici et là, en plus du bogue de propogation NaN, parfois des avertissements apparaissent et parfois ils ne le font pas

Une autre question, si NaN est le résultat à la fois de min / max s'il existe dans l'entrée, et que nous vérifions déjà isNan / invalid, ne devrait-il pas se terminer rapidement lors de la découverte de la première instance de NaN?

@tzickel aucune des opérations de réduction de sortie anticipée. Ils pourraient à l'avenir s'ils sont refactorisés en gufuncs.

Cette page vous a été utile?
0 / 5 - 0 notes