Numpy: BUG: np.min verbreitet nicht immer NaNs

Erstellt am 11. Jan. 2018  ·  65Kommentare  ·  Quelle: numpy/numpy

Diese Untersuchung ist darauf zurückzuführen, dass scipy CI in den letzten Tagen, dh nach der Veröffentlichung von 1.14, auf dem Appveyor zu

Auf meinem Heimcomputer (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

Auf meinem Arbeitscomputer (macOS, conda python 3.6.2, numpy über pip in einer sauberen Umgebung installiert):

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

Warum führen die ersten drei Beispiele bei den ersten Codebeispielen nicht zu einer Warnung, sondern nur zu den letzten?
Bei der zweiten Reihe von Beispielen wird überhaupt keine Warnung ausgegeben.

00 - Bug numpy.core

Alle 65 Kommentare

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

Ich denke, ein weiterer Punkt hier ist, dass es unterschiedliche Codepfade sein können, die ein anderes (meiner Meinung nach nicht wirklich numpy) Verhalten auslösen, z. B. aufgrund der Verwendung von AVX oder ähnlichem. Ich glaube, ich habe manchmal gesehen, dass Vektormaterial die Fehler-CPU-Flags nicht gesetzt hat, und da das etwas zufällig erscheinen kann, ob sie verwendet werden oder nicht ....

Keine Ahnung, ob das viel damit zu tun hat, aber ich gehe davon aus, dass numpy normalerweise nicht allzu viel gegen Gleitkommafehler macht, denke ich. Außer zu überprüfen, ob etwas passiert ist.

Und wie Chuck sagte, ist ein Großteil dieses Gleitkomma-Fehlerflags auf verschiedenen Systemen leider unterschiedlich.

Ok, verstanden, warum der Fehler nur bei einigen Installationen auftritt und nicht bei anderen.
Der Grund, warum scipy np.min für ein Array verwendet, das möglicherweise NaN enthält, liegt darin, dass es eine schnelle Möglichkeit ist, das Vorhandensein von NaN zu überprüfen. Die Numpy-Dokumentation legt nahe, dass dies zulässig ist:

NaN-Werte werden weitergegeben, dh wenn mindestens ein Element NaN ist, ist der entsprechende Mindestwert ebenfalls NaN.

Da dies einer der zulässigen Anwendungsfälle für np.min ist, würde ich aus Sicht der Verwendung nicht erwarten, dass überhaupt Warnungen / Fehler ausgegeben werden.

(Es gibt offensichtlich andere Möglichkeiten, dies zu erreichen, z. B. np.isnan(arr).any() , aber das ändert nichts an meiner obigen Argumentation.)

Überhaupt keine plausible Änderung, denke ich.

+1 für überhaupt keine Warnungen

Hallo,

Ich bin auf eine ähnliche Inkonsistenz gestoßen:

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

Warum erhöht eine Form von mehr als 8 diese Laufzeitwarnung?

@NotSqrt @seberg Ich

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

Zeigt dieses merkwürdige Verhalten unter OSX und Windows, aber nicht unter Linux. Neue virtuelle Umgebung mit Python 2.7.13 und 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

Sehen Sie die beiden Zeilen "Dies sollte gemäß Dokument: 19.0 nan sein".

Auch nb erscheint die Warnung nicht in numpy 1.13.1 (wo ich dieses Verhalten zum ersten Mal beobachtet habe.)

@kippr Woher hast du NumPy?

Ich gehe davon aus, dass die Grenze mit acht Elementen möglicherweise mit AVX512 zusammenhängt und das Problem eine Kombination aus Compiler und CPU ist. Auf welchem ​​CPU / Compiler sehen Sie das Problem?

@ Juliantaylor Gedanken?

Eine Ausrichtung kann ebenfalls Auswirkungen haben.

Hallo @charris

Vielen Dank, dass Sie sich meinen Beitrag angesehen haben. Um Ihre Fragen zu beantworten:

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

NumPy wurde über pip auf meinem Mac in einer neuen virtuellen Umgebung installiert (Python 2.7.13 über Homebrew installiert). Ich denke, es werden alle Standard-Compiler-Flags usw. verwendet.

Ich habe gerade die Installation von virtual env und reran pip neu erstellt, und hier ist, was für mich relevant erscheint, aber es gibt viele Meldungen, die bei der Installation eingehen. (Vollständiges Protokoll beigefügt.) Bitte lassen Sie mich wissen, wenn ich weitere Informationen habe kann aus dem Build-Verzeichnis oder so weiter fischen oder wenn es Kompilierungsflags gibt, die versucht werden müssen.

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

Vielen Dank
Kris

build.log

Ps wahrscheinlich keine Überraschung, aber das gleiche Verhalten tritt bei 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

Ich verwende dieselbe numpy-Version (auf arch) mit einer CPU, die auch über AVX2- und sse4.2-Funktionen verfügt, und sehe sie unter Linux nicht. Vielleicht hat es etwas mit Mac / Clang zu tun?

BEARBEITEN / Änderung: (Ich hatte ein bisschen versucht, verschiedene Ausrichtungen zu provozieren, habe den Warnungsteil nicht überprüft, aber den falschen Ergebnisteil sehe ich nicht.)

Ich vermute, dass es ein CPU-Flag gibt, das nicht richtig gesetzt wird, wahrscheinlich im Zusammenhang mit dem Compiler.

Wir sollten einen Test haben, der dieses Problem aufdeckt, wenn auch nur, um es zu verfolgen. @kippr Was macht

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

auf Ihrer Installation tun?

Hallo @charris

Das scheint in Ordnung zu sein:

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

Aber ich habe noch ein paar Combos ausprobiert und folgendes gefunden (siehe letztes, 8 Werte und Achse = 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])

Ziemlich mysteriös. Ich bin mir nicht sicher, ob das überhaupt hilft, die Ursache zu verstehen.

@seberg - ja das stimmt, ich habe dieses Verhalten auf meinen Macs und auch unter Windows beobachtet, aber Linux war in Ordnung.

Vielen Dank
Kris

@kippr Dies ist ein wirklich störender Fehler, da er eine grundlegende Operation betrifft und das Problem wahrscheinlich nicht von NumPy herrührt. Wir können versuchen, die Vektorisierung unter MAC und Windows zu deaktivieren und zu prüfen, ob dies hilfreich ist. Sehen Sie dieses Problem nur in Python 2.7?

Ich kann in NumPy nichts sehen, was dieses Verhalten für 1.14 geändert hätte. Vielleicht spielt OpenBLAS mit den Steuerelementen ...

@ juliantaylor @VictorRodriguez Gedanken?

Hallo @charris

Ich sehe dieses Verhalten auch in Python 3 auf meinem 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 Ich habe dieses Verhalten zum ersten Mal in NumPy Version 1.13 unter Python 2.7 auf meinem Mac bemerkt, daher war es keine in 1.14 eingeführte Regression:

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'

Und reproduzierte es in 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'

Aber sehen Sie dieses Problem nicht unter 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 Ich habe auf meiner Reise zu Leistungsoptimierungen für Numpy gesehen, dass Openblas und Compiler-Flags das Verhalten von Numpy / Scipy beeinflussen können (basierend auf meiner Erfahrung). Der erste Schritt, den wir unternehmen könnten, besteht darin, dies in einem C-Test (unter Verwendung von Openblas-Bibliotheken) anzugehen, damit wir das Verhalten isolieren und prüfen können, ob es repliziert werden kann. Auch nur um zu überprüfen, ob dies nur auf MAC und Windows ist, oder? Ich kann dieses Verhalten unter Linux nicht sehen (wenn die AvX-Patches für 1.15 verfügbar sind, sollten sie dies nicht beeinflussen). @kippr Wie hast du deine Nummer in Mac / Windows erstellt? Grüße

Hallo @ VictorRodriguez

Dies erfolgte per Pip-Installation auf beiden Plattformen, eine neue virtuelle Umgebung im Mac-Fall. Siehe oben, wo ich das Protokoll für pip einschließlich der Kompilierungsausgabe für die Installation von Python 2.7 eingefügt habe. (Die Installation von Python 3 pip scheint ein Rad zu sein.)

Prost Kris

Hallo Leute
Weitere Informationen, die ich zur Verfügung stellen kann, um zu helfen?

Vielen Dank

Auf dem Mac (und möglicherweise auch auf anderen Plattformen) tritt der Fehler auf, weil der Compiler (auf dem Mac ist es standardmäßig clang / llvm nicht gcc) die SSE2-Befehle auf problematische Weise neu anordnet. Der Fehler in:

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

geschieht, weil der Code, der ausgeführt wird, der sse2_minimum_DOUBLE ist, der generiert wird aus:
https://github.com/numpy/numpy/blob/d7d5cb3feccc1fc6cf57159e8b9fe0a733968706/numpy/core/src/umath/simd.inc.src#L1020

Schauen wir uns diesen Code (ich habe das SSE2-Formular automatisch erweitert) in der Funktion in Zeile 1041 an:

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

Der Compiler versteht nicht, dass die Zuweisung c1 und npy_get_floatstatus () zusammenhängen, und ändert den Code daher in:

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

Das ist natürlich unsinnig ... Ich weiß nicht, was der empfohlene Weg ist, um dies unter Optimierungen nicht zu tun. (Ich denke, andere Plattformen haben dafür STDC FENV_ACCESS Pragma?)

Vielleicht, da die Änderungen in diesem Code 5 Jahre alt sind, wird der Fehler durch eine neue Version von clang und verschiedene Optimierungen verursacht?

Danke @tzickel

Ich habe C seit einer Highschool-Klasse vor vielen Monden nicht mehr geschrieben, daher werde ich definitiv nicht herausfinden, wie ich das klug lösen kann, aber ich habe ein paar Dinge versucht, um den Compiler zu zwingen, in der ursprünglichen Reihenfolge bis zu bewerten Ändern der if-Anweisung von oben in:

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

Und tatsächlich verschwindet der Fehler, sobald ich dies tue.

(Ich habe auch eine Reihe anderer weniger dummer Formulare ausprobiert, um den Compiler dazu zu bringen, _mm_min_pd vor der if-Anweisung auszuwerten, indem ich beispielsweise auf beiden Seiten der if-Anweisung und nach dem if / else-Block Verweise auf c1 eingefügt habe Ich vermute, dass es sie entweder trotzdem neu geordnet hat, aber immer die Zuweisung c = _mm_min_pd ausgeführt hat, oder dass meine Anrufe tatsächlich Noops der einen oder anderen Form waren ..)

Aber auf jeden Fall kann ich bestätigen, dass Sie den Fehler identifiziert haben, den ich sehe. Hoffen wir, dass jemand eine gute Möglichkeit hat, dem Compiler einen Hinweis zu geben, die Reihenfolge von _mm_min_pd und npy_get_floatstatus in Ruhe zu lassen.

Ok, eine andere Sache, die Google vorgeschlagen hat, hat auch funktioniert: Markieren von c1 flüchtig:

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

wird:

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

Aber ich bin mir nicht sicher, welche Auswirkungen dies hat, wenn dies der beste Weg ist, um das Ziel zu erreichen.

Vielen Dank für das zusätzliche Debugging auf diesem @tzickel. Dies war ein sehr tiefgreifendes Debugging. @Kippr Ich habe eine Frage. Sie erwähnen, dass es sich um eine neue Pip-Installation auf einem Mac handelt. Sie sollte also nicht von Grund auf neu erstellt werden, oder? Auch wenn ich auf meinem Linux-System mit gcc lief:

$ python
Python 3.6.5 (Standard, 1. April 2018, 15:40:54)
[GCC 7.3.0] unter Linux
Geben Sie "Hilfe", "Copyright", "Credits" oder "Lizenz" ein, um weitere Informationen zu erhalten.

importiere numpy als 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 = 'erhöhen')
{'dividieren': 'warnen', 'über': 'warnen', 'unter': 'ignorieren', 'ungültig': 'warnen'}
np.min ([np.nan, 1.0])
nan

und sehen Sie überhaupt keine Warnungen, also könnte das Problem, wie beschrieben, mit Klirren sein. Das einzige, was mich wundert, ist, warum Sie dies mit pip install numpy sehen. Wie wurde es gebaut? und welche Flaggen wurden verwendet?

Auch kein Fehler mit meinem Mac:

https://hastebin.com/cuzinajero.swift

Ich frage mich, ob dies mit so etwas sicherer gemacht werden könnte

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

@ Juliantaylor Gedanken?

Oder auch

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

Der Kommaoperator soll ein Sequenzpunkt sein, obwohl er möglicherweise nicht threadsicher ist. Hmm, ich frage mich , wie viel in dem SIMD - Code Thread - sicher ist.

@ VictorRodriguez Wenn ich pip install numpy auf meinem Mac mit python2.7 ausführe, wird es aus dem Quellcode erstellt. Wenn ich pip install mit python3 ausführe, wird es von einem Rad aus installiert.

Ich habe die vollständige Ausgabe der Pip-Installation erfasst: https://github.com/numpy/numpy/files/1912086/build.log

Um die obigen Änderungen zu testen, habe ich aus der Quelle (aktueller Master) erstellt, aber überprüft, dass ich ohne die Optimierung immer noch das fehlerhafte Verhalten sah. Beim Erstellen habe ich die in INSTALL.rst.txt vorgeschlagene Befehlszeile verwendet:
python setup.py build -j 4 install --prefix $HOME/.local

Ein Wort der Vorsicht, jemand hier sagt, er habe den Fehler auch in Fenstern gesehen. Ich gehe davon aus, dass es nicht mit Clang on Windows kompiliert wurde, aber mit MSVC oder GCC könnte dies bedeuten, dass Numpy die Regeln der Plattform nicht respektiert. In Windows verwendet numpy _statusfp, um den fp-Status abzurufen. In der MSDN-Dokumentation heißt es:

Viele Funktionen der Mathematikbibliothek ändern das Gleitkomma-Statuswort mit unvorhersehbaren Ergebnissen. Durch die Optimierung können Gleitkommaoperationen für Aufrufe von _status87, _statusfp und verwandten Funktionen neu angeordnet, kombiniert und eliminiert werden. Verwenden Sie die Compileroption / Od (Disable (Debug)) oder die Pragma-Direktive fenv_access, um Optimierungen zu verhindern, die Gleitkommaoperationen neu anordnen.

GCC und MSVC (aber vorerst nicht klirrend) haben ein Pragma, um dies zu kontrollieren.

@tzickel ziemlich sicher, dass der Windows-Build sagte, dass es mit Cygwin war. Es wird für mich mit MSVC nicht reproduziert.

Insgesamt gefällt mir der Vorschlag von

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

Dies wird sogar vom Visual Studio 8-Compiler unterstützt, der für Python 2.7 verwendet wird
Der zweite, kompaktere Vorschlag scheint zu verschleiert

Gibt es andere Stellen im Code, die für diesen Fehler anfällig sind?

  1. Der Cygwin-Build sagt GCC, was bedeutet, dass der Fehler wie Linux wirken sollte, nein?

  2. Wenn ich richtig verstehe, hilft der Kommaoperator nicht bei Optimierungen, sondern funktioniert auf einer anderen Ebene der Codegenerierung. Dem Compiler steht es weiterhin frei, zu glauben, dass beide Ausdrücke nichts miteinander zu tun haben, und sie zu verschieben. Hier ist ein kleines Beispiel, um zu zeigen, dass dies der Fall ist (versuchen Sie, den Kommentarcode mit der Zeile daneben zu ändern), überprüfen Sie das minpd in Bezug auf den Aufruf der externen Funktion (stellen Sie sicher, dass -O3 in Compiler-Flags angegeben ist) und wechseln Sie zwischen GCC und CLANG (scheint, dass der GCC-Trunk vorerst den gleichen Fehler hat :)):

https://godbolt.org/g/Zoc5xr

  1. Wenn Sie sich fenv.h ansehen, hat grundsätzlich jede Funktion im Code, die auf diese Datei zugreift, das gleiche Problempotential. Alles, was im Code von diesem aufgerufen wird, wird in numpy / core / src / npymath / ieee754.c verwendet

  2. Meiner Meinung nach kann clang derzeit keinen sicheren Code für diese Art von Funktionen mit Optimierungen erstellen. Daher stehen folgende Optionen zur Verfügung:
    A. Kompilieren Sie mit gcc (mindestens die offiziellen Mac-Räder) und geben Sie eine Warnung aus, wenn Sie mit clang kompiliert werden.
    B. Clang unterstützt ein Optnone-Attribut pro Funktion, das auf solche Funktionen gestreut werden kann, um alle Optimierungen (langsamer, aber korrekter Code) zu deaktivieren, wenn es mit clang kompiliert wird:
    https://clang.llvm.org/docs/AttributeReference.html#optnone -clang-optnone-clang-optnone

Das Wechseln zum Komma-Operator hilft überhaupt nicht - ; ist bereits ein Sequenzpunkt.

@ Eric-Wieser Bist du sicher? Ich sehe ; als Sequenzpunkt. https://msdn.microsoft.com/en-us/library/azk8zbxd.aspx .

In jedem Fall ist die Reihenfolge garantiert, in der die Argumente eines Kommaoperators ausgewertet werden, was bei Anweisungen, die durch ; getrennt sind, nicht der Fall ist.

@charris Leider scheint die vorgeschlagene Änderung das Problem nicht zu lösen:

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 {

Hinweis: Das Markieren der Variablen als flüchtig behebt das Problem.

Lassen Sie mich wissen, wenn Sie andere Varianten ausprobieren möchten.

Es scheint, dass Compiler die Spezifikation in Bezug auf Kommaoperatoren nicht einhalten oder zumindest so, wie wir die Spezifikation verstehen. Die einfachste Lösung für den aktuellen Status von Compilern scheint das Hinzufügen von volatile , obwohl es brüchig erscheint.

Nun, GCC 8.1 wurde gerade veröffentlicht und raten Sie mal, was ... GCC mit Optimierungen erzeugt jetzt das gleiche Problem wie Clang hier https://github.com/numpy/numpy/issues/10370#issuecomment -384154230 (und das Komma nicht Hilfe dort wie erwartet, aber das flüchtige tut es), obwohl ich nicht weiß, ob distutils in gcc-Flags aktiviert, die dies mildern, würde ich nicht denken.

Hier ist der Code für GCC 8.1 (Sie können mit 7.3 vergleichen, wo es in Ordnung ist):
https://godbolt.org/g/AJRdRQ

Suchen Sie dort im asm nach dem Aufruf minpd und npy_get_floatstatus.

Lassen Sie uns vorerst mit volatile zusammen mit einigen Tests gehen, die hoffentlich ein Problem signalisieren sollten, wenn es auftritt. Eine andere Option, die wir uns ansehen könnten, ist eine separate Funktion, bei der wir flüchtige oder Compiler-Direktiven verwenden können, diese jedoch an einem Ort haben, um die Verwaltung zu vereinfachen.

Dies ist ein viel größerer Fall als dieses spezielle Problem. Der gesamte Numpy-Code, der Funktionen in ieee754.c aufruft, muss überprüft und repariert werden. (Vielleicht benennen Sie diese Ausgabe noch einmal um oder öffnen Sie noch besser eine neue Ausgabe).

Das überraschende Verhalten der minpd-Anweisung kann für https://github.com/numpy/numpy/issues/10370#issuecomment -381241813 relevant sein:

Wenn nur ein Wert ein NaN (SNaN oder QNaN) für diesen Befehl ist, wird der zweite Operand (Quelloperand), entweder ein NaN oder ein gültiger Gleitkommawert, in das Ergebnis geschrieben.

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

@ Mattkretz scheint nicht so. (das ist das erwartete Verhalten und warum diese Prüfung bereits vorhanden ist).

@tzickel Aus Neugier tritt auf welcher Optimierungsstufe das Problem auf? Früher haben wir uns auf -O2 , aber ich denke, einige Plattformen haben -O3 oder ein gleichwertiges Produkt verwendet.

Ähnlich wie bei diesem gcc-Fehler denke ich: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=6065

Es scheint, als ob das Setzen von #pragma STDC FENV_ACCESS ON hier die richtige Lösung ist, aber es erfordert C99, für dessen Verwendung wir Python 2.7 löschen müssen. Vielleicht geht der GCC --std=c90 -Modus davon aus, dass er die Optimierung durchführen kann, weil das Pragma fehlt

Für alle Ihre Fragen gehen Sie einfach auf den Link hier:

https://godbolt.org/g/AJRdRQ (* es scheint geteilt zu sein, also kopiere die Eingabe vielleicht in ein neues Fenster)

und versuchen Sie, die Compiler-Version, die Compiler-Flags und den Code zu ändern und das Ergebnis sofort zu sehen ...

Dies geschieht, wenn auf beiden neuesten Compilern sogar -O1 verwendet wird. Das #pragma scheint nichts zu tun ....

Diese Version ist sehr kompliziert, es gibt zu viele Assembler und es ist schwierig, den Assembler mit dem c-Code in Beziehung zu setzen. Die ursprüngliche Version war viel einfacher und demonstrierte auch das Problem.

Siehe auch die PR

hmm .... kann mir jemand sagen auf welchem ​​OS / Compiler numpy-1.14.3-cp27-cp27mu-manylinux1_x86_64.whl (aus dem pypi-Archiv) kompiliert wurde? Ich denke, hier könnte sich auch ein anderes (verwandtes) Problem verstecken.

@tzickel Ich glaube, der standardmäßige vertrauenswürdige Ubuntu-Compiler ist gcc 4.8. Ich kann nicht sehen, dass etwas

@charris Auf dem Manylinux-Container wird Centos5 mit installiertem gcc 4.2 ausgeführt.

@ngoldbaum Danke für die Info. Wir müssen bald Räder für Python 3.7 bauen. Ist das so einfach wie das Hinzufügen eines MB_PYTHON_VERSION=3.7 -Eintrags zur Build-Matrix von travis.yml?

Ich denke, Sie müssen warten, bis das Manylinux-Tool aktualisiert wird. Das dauerte einige Wochen, nachdem python3.6 IIRC herauskam. @njsmith weiß wahrscheinlich mehr.

@ngoldbaum Was hat die Wahl von gcc 4.2 getrieben ? Ist es einfach, eine spätere Compilerversion zu verwenden, wenn wir dies wünschen?

Was hat die Wahl von gcc 4.2 vorangetrieben?

Ich glaube, das Ziel war es, das Kompilieren mit einem Glibc zu ermöglichen, das alt genug ist, damit Probleme mit der Binärkompatibilität in der Praxis kein Problem darstellen

Ist es einfach, eine spätere Compilerversion zu verwenden, wenn wir dies wünschen?

Ich weiß es nicht. Ich weiß auch nicht, wie numpy es schafft, Räder zu bauen. Ich verwende Matthew Bretts Multibuild-Projekt für meine Projekte und muss warten, bis es aktualisiert ist, um Python3.7-Räder für meine Projekte zu erstellen.

Ok, ich bin mir nicht sicher, ob es sich um das endgültige Problem handelt, aber ich glaube, ich habe in diesem SSE-Code ein anderes Problem gefunden:

In numpy 1.14.0 wurde Code hinzugefügt, um die Laufzeitwarnungen auszulösen, wenn FP-Fehler vorliegen:
https://github.com/numpy/numpy/commit/d47ca7b26172c42b01c3132d0e46e70578c8ea21

Aber wenn wir uns die SSE-Implementierung noch einmal ansehen:
https://github.com/numpy/numpy/blob/d7d5cb3feccc1fc6cf57159e8b9fe0a733968706/numpy/core/src/umath/simd.inc.src#L1020

Wir können sehen, dass es durch die FPU nur den mittig ausgerichteten Teil des Arrays passiert. Der Header und der Trailer (die nicht SSE-fähig sind, weil sie nicht speicherausgerichtet sind) werden manuell auf NaN überprüft und nicht über die FPU, während die SSE Teile tun dies, daher löst nur der mittlere Teil eine NaN-Warnung aus, während der andere (während er in Eingabe und Ausgabe gleichwertig ist) dies nicht tut. Ist das in Ordnung ?

np.min ([1, np.nan, 1, 1, 1, 1, 1, 1]) löst keine Laufzeitwarnung aus
aber np.min ([1, 1, np.nan, 1, 1, 1, 1, 1]) tut es.

np.min ([1, np.nan, 1, 1, 1, 1, 1, 1]) löst keine Laufzeitwarnung aus

@tzickel das hängt mit # 11029 zusammen, oder?

Bearbeiten: Formatierung

Es scheint, dass das Quellproblem # 8954 war, was zu PR # 9020 führte

Ja, aber mein Punkt ist, dass # 9020 nicht alle möglichen Fälle abdeckte. Einer davon ist dieser SSE-Code (der diesen Mechanismus für einige Optimierungen untergräbt).

Was # 11029 betrifft, versuche ich herauszufinden, warum in den Posts hier und da neben dem NaN-Propagierungsfehler manchmal Warnungen angezeigt werden und manchmal nicht

Eine andere Frage: Wenn NaN das Ergebnis von min / max ist, wenn es in der Eingabe vorhanden ist, und wir bereits prüfen, ob es nicht / ungültig ist, sollte es nicht schnell beendet werden, wenn die erste Instanz von NaN entdeckt wird?

@tzickel keine der Reduktionsoperationen vorzeitig

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen