Numpy: BUG: np.min tidak selalu menyebarkan NaN

Dibuat pada 11 Jan 2018  ·  65Komentar  ·  Sumber: numpy/numpy

Investigasi ini muncul dari CI scipy mulai gagal pada appveyor dalam beberapa hari terakhir, yaitu setelah 1,14 dirilis.

Di komputer rumah saya (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

Di komputer kerja saya (macOS, conda python 3.6.2, numpy diinstal melalui pip di env bersih):

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

Dengan kumpulan contoh kode pertama, mengapa tiga contoh pertama tidak menghasilkan peringatan, hanya yang terakhir?
Dengan set contoh kedua, tidak ada peringatan sama sekali.

00 - Bug numpy.core

Semua 65 komentar

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

Saya pikir poin lain di sini adalah bahwa mungkin jalur kode berbeda yang memicu perilaku yang berbeda (menurut saya tidak terlalu numpy), misalnya karena menggunakan AVX atau semacamnya, saya pikir saya kadang-kadang melihat bahwa barang-barang vektor gagal untuk mengatur tanda kesalahan cpu, dan karena itu bisa tampak agak acak jika digunakan atau tidak ....

Entah apakah itu ada hubungannya dengan itu, tetapi saya mengharapkannya, numpy tidak melakukan terlalu banyak tentang kesalahan floating point biasanya saya pikir. Kecuali memeriksa apakah sesuatu telah terjadi.

Dan seperti yang dikatakan Chuck, sayangnya banyak dari flag error floating point ini berbeda pada sistem yang berbeda.

Ok, mengerti mengapa kesalahan hanya terjadi pada beberapa instalasi dan bukan yang lain.
Alasan mengapa scipy menggunakan np.min pada array yang mungkin berisi NaN adalah karena ini cara cepat untuk memeriksa keberadaan mereka. Dokumentasi numpy menunjukkan bahwa ini diperbolehkan:

Nilai NaN disebarkan, yaitu jika setidaknya satu item adalah NaN, nilai min yang sesuai akan menjadi NaN juga.

Mengingat bahwa ini adalah salah satu kasus penggunaan yang diizinkan untuk np.min, dari sudut pandang penggunaan saya tidak akan mengharapkan peringatan / kesalahan apa pun untuk dikeluarkan sama sekali.

(Jelas ada cara lain untuk mencapai ini, misalnya np.isnan(arr).any() , tetapi itu tidak mengubah alasan saya di atas)

Kurasa tidak sama sekali perubahan yang masuk akal.

1 untuk tidak ada peringatan sama sekali

Hai,

Saya menemukan ketidakkonsistenan serupa:

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

Mengapa bentuk yang lebih dari 8 memunculkan RuntimeWarning itu?

@NotSqrt @seberg Saya melihat perilaku serupa di mana min tidak menyebarkan NaN dengan benar sama sekali, ketika ukuran array input mencapai ukuran 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])

Menunjukkan perilaku yang agak aneh ini di OSX & Windows, tetapi bukan Linux .. virtualenv baru menggunakan python 2.7.13 dan 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

Lihat dua baris "this should be nan as per docs: 19.0 " di sana?

Juga nb peringatan tidak muncul di numpy 1.13.1 (dimana saya pertama kali mengamati perilaku ini.)

@kippr Dari mana Anda mendapatkan NumPy?

Saya akan menebak batas delapan elemen mungkin terkait dengan AVX512 dan masalahnya adalah beberapa kombinasi dari compiler dan cpu. Pada cpus / compiler apa Anda melihat masalahnya?

@juliantlor Pikiran?

Penjajaran mungkin juga berpengaruh.

Hai @arri

Terima kasih telah melihat posting saya .. untuk menjawab pertanyaan Anda:

CPU :
❃ sysctl -n machdep.cpu.brand_string Intel (R) Core (TM) i7-6700K CPU @ 4.00GHz

NumPy diinstal melalui pip di mac saya di virtualenv baru (python 2.7.13 diinstal melalui homebrew), jadi saya kira itu menggunakan semua bendera compiler default dll?

Saya baru saja membuat ulang virtual env dan reran pip install, dan inilah yang terlihat relevan bagi saya, tetapi ada banyak pesan yang datang saat menginstal .. (log lengkap terlampir.) Tolong beri tahu saya jika ada info lain yang saya bisa keluar dari direktori build atau yang lainnya, atau jika ada flag compile untuk dicoba.

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

Terima kasih
Keris

build.log

Ps mungkin tidak mengherankan, tetapi perilaku yang sama muncul dengan 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

Saya menggunakan versi numpy yang sama (di arch) dengan CPU yang juga memiliki kemampuan AVX2 dan sse4.2 dan tidak melihatnya di linux. Mungkin ada hubungannya dengan mac / clang?

EDIT / Amandemen: (Saya telah mencoba sedikit dengan memprovokasi keselarasan yang berbeda, tidak memeriksa bagian peringatan, tetapi bagian hasil yang salah saya tidak melihat)

Saya menduga ada flag cpu yang tidak disetel dengan benar, mungkin terkait kompilator.

Kami harus memiliki tes yang menemukan masalah ini, jika hanya untuk melacaknya. @kippr Apa

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

lakukan pada instalasi Anda?

Hai @arri

Sepertinya baik-baik saja:

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

Tetapi saya mencoba beberapa kombo lagi dan menemukan yang berikut (lihat yang terakhir, 8 nilai dan sumbu = 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])

Cukup misterius .. tidak yakin apakah itu membantu memahami sebab sama sekali.

@seberg - ya itu benar, saya mengamati perilaku ini di Mac saya dan juga di Windows, tetapi linux baik-baik saja.

Terima kasih
Keris

@kippr Ini adalah bug yang sangat mengganggu, karena ini mempengaruhi operasi dasar dan masalahnya mungkin tidak berasal dari NumPy. Kami dapat mencoba mematikan vektorisasi pada MAC dan Windows dan melihat apakah itu membantu. Apakah Anda hanya melihat masalah ini di Python 2.7?

Saya tidak dapat melihat apa pun di NumPy yang akan mengubah perilaku ini untuk 1,14. Mungkin OpenBLAS mengutak-atik kontrol ...

@juliantlor @VictorRodriguez Thoughts?

Hai @arri

Saya melihat perilaku ini juga di python 3 di Mac saya:

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 Saya pertama kali memperhatikan perilaku ini di NumPy versi 1.13 di bawah python 2.7 di Mac saya, jadi itu bukan regresi yang diperkenalkan di 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'

Dan mereproduksinya di 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'

Tapi jangan lihat masalah ini di 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 Saya telah melihat dalam perjalanan saya dalam pengoptimalan performa untuk numpy bahwa openblas dan flag compiler dapat memengaruhi perilaku numpy / scipy (berdasarkan pengalaman saya). Langkah pertama yang bisa kita ambil adalah menangani ini pada tes C (menggunakan pustaka openblas) sehingga kita dapat mengisolasi perilaku dan melihat apakah itu dapat direplikasi. Juga hanya untuk memeriksa ulang ini hanya di MAC dan windows kan? saya tidak bisa melihat perilaku ini di Linux (fwiw patch avx masuk ke 1,15, jadi mereka seharusnya tidak mempengaruhi ini). @kippr bagaimana Anda membangun numpy Anda di mac / windows? salam

Hai @Victor

Ini dilakukan melalui pip install di kedua platform, virtualenv baru dalam casing Mac. Lihat di atas di mana saya menempelkan log untuk pip termasuk output kompilasi untuk instalasi python 2.7. (Pemasangan Python 3 pip tampaknya seperti roda.)

Cheers Kris

Halo semuanya
Ada info lebih lanjut yang bisa saya berikan untuk membantu?

Terima kasih

Di mac (dan mungkin juga platform lain) bug terjadi karena kompiler (di mac itu clang / llvm bukan gcc secara default) menyusun ulang perintah SSE2 dengan cara yang bermasalah. Bug di:

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

terjadi karena kode yang dijalankan adalah sse2_minimum_DOUBLE yang dihasilkan dari:
https://github.com/numpy/numpy/blob/d7d5cb3feccc1fc6cf57159e8b9fe0a733968706/numpy/core/src/umath/simd.inc.src#L1020

Secara khusus mari kita lihat kode ini (saya telah memperluas formulir SSE2 secara otomatis) di dalam fungsi di baris 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);
            ....
         }

Kompilator tidak memahami bahwa tugas c1 dan npy_get_floatstatus () terkait, dan dengan demikian mengubah kode menjadi:

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

yang tentu saja tidak masuk akal ... Saya tidak tahu cara apa yang disarankan untuk membuatnya tidak melakukan ini dalam pengoptimalan. (Saya pikir platform lain memiliki pragma STDC FENV_ACCESS untuk ini?)

Mungkin karena perubahan dalam kode ini sudah 5 tahun, bug tersebut disebabkan oleh versi baru clang dan pengoptimalan yang berbeda?

Terima kasih @tzickel

Saya belum menulis C sejak kelas sekolah menengah beberapa bulan yang lalu, jadi saya pasti tidak akan menemukan cara cerdas untuk menyelesaikan ini, tetapi saya mencoba beberapa hal untuk memaksa penyusun mengevaluasi dalam urutan aslinya dengan mengubah pernyataan if di atas menjadi:

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

Dan memang, begitu saya melakukan ini, bug tersebut menghilang.

(Saya juga mencoba banyak bentuk lain yang kurang bodoh untuk mencoba membuat kompiler mengevaluasi _mm_min_pd sebelum pernyataan if, misalnya dengan meletakkan referensi ke c1 di kedua sisi pernyataan if dan setelah blok if / else, semua ke tidak berhasil. Saya menduga itu akan mengubah urutannya tetapi selalu menjalankan tugas c = _mm_min_pd, atau menemukan bahwa panggilan saya sebenarnya noops dari satu bentuk atau lainnya ..)

Tetapi bagaimanapun saya dapat mengonfirmasi bahwa Anda telah mengidentifikasi bug yang saya lihat, semoga seseorang memiliki cara yang baik untuk memberi petunjuk kepada kompiler untuk membiarkan urutan _mm_min_pd dan npy_get_floatstatus saja ..

Ok, satu hal lain yang disarankan Google agar juga berfungsi: menandai c1 volatile:

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

menjadi:

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

Tapi saya tidak yakin apa implikasinya sebagai / apakah itu cara terbaik untuk mencapai tujuan.

Terima kasih banyak atas debug ekstra pada @tzickel ini, ini adalah debug yang sangat dalam, @kippr Saya punya pertanyaan, Anda menyebutkan bahwa itu adalah pemasangan pip baru di Mac, jadi tidak seharusnya membangun dari awal kan? Juga jika saya menjalankan sistem Linux saya dengan gcc:

$ python
Python 3.6.5 (default, 1 Apr 2018, 15:40:54)
[GCC 7.3.0] di linux
Ketik "bantuan", "hak cipta", "kredit" atau "lisensi" untuk informasi lebih lanjut.

impor numpy sebagai 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 (semua = 'naikkan')
{'divide': 'warn', 'over': 'warn', 'under': 'ignore', 'invalid': 'warn'}
np.min ([np.nan, 1.0])
nan

dan tidak melihat peringatan sama sekali, jadi mungkin seperti yang dijelaskan masalahnya mungkin ada pada dentang. satu-satunya hal yang membuat saya bertanya-tanya adalah mengapa Anda melihat ini dengan pip install numpy. Bagaimana itu dibangun? dan bendera apa yang digunakan?

Juga tidak ada kesalahan dengan mac saya:

https://hastebin.com/cuzinajero.swift

Saya ingin tahu apakah ini bisa dibuat lebih aman dengan sesuatu seperti

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

@juliantlor Pikiran?

Atau bahkan

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

Operator koma seharusnya menjadi titik urutan, meskipun mungkin tidak aman untuk thread. Hmm, saya bertanya-tanya berapa banyak kode simd yang thread safe.

@VictorRodriguez jika saya menjalankan pip install numpy di mac saya dengan python2.7, itu dibangun dari sumber. Jika saya menjalankan pip install dengan python3, itu menginstal dari roda.

Saya menangkap output penuh dari pemasangan pip: https://github.com/numpy/numpy/files/1912086/build.log

Untuk menguji perubahan di atas, saya membangun dari sumber (master saat ini), tetapi memeriksa bahwa tanpa tweak saya masih melihat perilaku yang rusak. Saat membangun itu saya menggunakan baris perintah yang disarankan di INSTALL.rst.txt:
python setup.py build -j 4 install --prefix $HOME/.local

Sebuah kata hati-hati, seseorang di sini mengatakan dia melihat bug di jendela juga. Saya berasumsi bahwa itu tidak dikompilasi dengan clang on windows, tetapi dengan MSVC atau GCC, ini mungkin berarti numpy tidak menghormati aturan platform. Di Windows, numpy menggunakan _statusfp untuk mendapatkan status fp, dokumentasi MSDN menyatakan:

Banyak fungsi pustaka matematika memodifikasi kata status floating-point, dengan hasil yang tidak dapat diprediksi. Pengoptimalan dapat menyusun ulang, menggabungkan, dan menghilangkan operasi floating-point di sekitar panggilan ke _status87, _statusfp, dan fungsi terkait. Gunakan opsi compiler / Od (Disable (Debug)) atau perintah pragma fenv_access untuk mencegah pengoptimalan yang menyusun ulang operasi floating-point.

GCC dan MSVC (tapi tidak clang untuk saat ini) memiliki pragma untuk mengontrol ini.

@tzickel cukup yakin windows build mengatakan itu dengan cygwin. Itu tidak mereproduksi untuk saya dengan MSVC.

secara keseluruhan saya suka saran @charris , ini standar C, tidak memerlukan pragma, dan AFAICT tidak menambahkan biaya tambahan apa pun

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

yang didukung bahkan oleh kompiler visual studio 8 yang digunakan untuk python 2.7
yang kedua, saran yang lebih ringkas tampaknya terlalu dikaburkan

Apakah ada tempat lain dalam kode yang rentan terhadap bug ini?

  1. Cygwin build mengatakan GCC yang berarti bug harus bertindak seperti linux, bukan?

  2. Jika saya mengerti dengan benar, operator koma tidak membantu pengoptimalan, ia bekerja di tingkat lain dari pembuatan kode. Kompilator masih bebas untuk berpikir bahwa kedua ekspresi tidak berhubungan dan memindahkannya. Berikut adalah contoh kecil untuk menunjukkan ini masalahnya (coba ubah kode komentar dengan baris di sebelahnya), periksa minpd relatif terhadap panggilan ke fungsi luar (pastikan -O3 dalam bendera kompilator diberikan), dan beralih di antara GCC dan CLANG (sepertinya GCC trunk memiliki bug yang sama untuk saat ini :)):

https://godbolt.org/g/Zoc5xr

  1. Jika Anda melihat fenv.h, pada dasarnya setiap fungsi dalam kode yang mengakses file itu memiliki potensi masalah yang sama. Jadi semua yang dipanggil dalam kode dari yang digunakan di numpy / core / src / npymath / ieee754.c

  2. Menurut pendapat saya, clang saat ini tidak dapat menghasilkan kode yang aman untuk jenis fungsi ini dengan pengoptimalan, jadi opsinya adalah:
    A. Kompilasi dengan gcc (setidaknya roda mac resmi), dan menghasilkan peringatan jika dikompilasi dengan dentang.
    B.Dentang mendukung atribut optnone per fungsi, yang dapat ditaburkan pada fungsi tersebut untuk menonaktifkan semua pengoptimalan (kode yang lebih lambat tapi benar) jika dikompilasi dengan clang:
    https://clang.llvm.org/docs/AttributeReference.html#optnone -clang-optnone-clang-optnone

Beralih ke operator koma tidak akan membantu sama sekali - ; sudah menjadi titik urutan.

@ eric-wieser Anda yakin? Saya tidak melihat ; terdaftar sebagai titik urutan. https://msdn.microsoft.com/en-us/library/azk8zbxd.aspx .

Bagaimanapun, urutan di mana argumen dari sebuah operator koma dievaluasi dijamin, tidak demikian halnya dengan pernyataan yang dipisahkan oleh ; ,

@charris sayangnya, perubahan yang disarankan tampaknya tidak menyelesaikan masalah:

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 yang menandai variabel sebagai volatile memperbaiki masalah.

Beri tahu saya jika Anda ingin mencoba varian lain.

Sepertinya penyusun tidak menganut spek mengenai operator koma, atau setidaknya seperti yang kita pahami speknya. Solusi paling sederhana untuk kondisi compiler saat ini tampaknya menambahkan volatile , meskipun tampaknya rapuh.

Nah, GCC 8.1 baru saja dirilis dan coba tebak ... GCC dengan pengoptimalan sekarang menghasilkan masalah yang sama dengan yang dilakukan clang di sini https://github.com/numpy/numpy/issues/10370#issuecomment -384154230 (dan koma tidak membantu di sana seperti yang diharapkan, tetapi volatile tidak), meskipun saya tidak tahu apakah distutils mengaktifkan flag gcc yang mengurangi hal ini, menurut saya tidak.

Berikut adalah kode pada GCC 8.1 (Anda dapat membandingkan dengan 7.3 jika tidak masalah):
https://godbolt.org/g/AJRdRQ

Cari di sana untuk panggilan minpd dan npy_get_floatstatus di asm.

Mari kita pergi dengan volatile untuk sementara waktu bersama dengan beberapa tes yang diharapkan akan menandakan masalah jika muncul. Opsi lain yang mungkin kita lihat adalah fungsi terpisah di mana kita dapat menggunakan arahan volatile atau kompiler, tetapi memilikinya di satu tempat untuk membuatnya lebih mudah dikelola.

Ini adalah kasus yang jauh lebih besar daripada masalah khusus ini. Semua kode numpy yang memanggil fungsi di ieee754.c perlu diaudit dan diperbaiki. (Mungkin ganti nama masalah ini lagi, atau lebih baik lagi, buka masalah baru).

Perilaku mengejutkan dari instruksi minpd mungkin relevan untuk https://github.com/numpy/numpy/issues/10370#issuecomment -381241813:

Jika hanya satu nilai NaN (SNaN atau QNaN) untuk instruksi ini, operan kedua (operan sumber), baik NaN atau nilai floating-point yang valid, ditulis ke hasil.

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

@TwitAsahOtak . (itulah perilaku yang diharapkan dan mengapa pemeriksaan ini sudah dilakukan).

@tzickel Karena penasaran, pada tingkat pengoptimalan manakah masalah tersebut muncul? Kami biasanya membatasi diri hingga -O2 , tetapi saya pikir beberapa platform telah menggunakan -O3 atau yang setara.

Mirip dengan bug gcc ini, menurut saya: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=6065

Sepertinya pengaturan #pragma STDC FENV_ACCESS ON adalah solusi yang tepat di sini, tetapi ini membutuhkan C99, yang kita perlu drop python 2.7 untuk menggunakannya. Mungkin mode GCC --std=c90 mengasumsikan dapat melakukan pengoptimalan karena pragma tidak ada

Untuk semua pertanyaan Anda, cukup buka tautan di sini:

https://godbolt.org/g/AJRdRQ (* sepertinya dibagikan, jadi salin masukan ke jendela baru mungkin)

dan coba ubah versi kompilator, bendera dan kode kompilator, dan lihat hasilnya dengan cepat ...

Ini terjadi saat menggunakan genap -O1 pada kedua kompiler terbaru. #Pragma sepertinya tidak melakukan apa-apa ....

Versi itu sangat rumit, terlalu banyak assembler dan sulit untuk menghubungkan assembler dengan kode c. Versi aslinya jauh lebih sederhana dan juga menunjukkan masalahnya.

Lihat juga bagian PR

hmm .... adakah yang bisa memberi tahu saya tentang OS / kompiler mana numpy-1.14.3-cp27-cp27mu-manylinux1_x86_64.whl (dari arsip pypi) dikompilasi? Saya pikir mungkin ada masalah (terkait) lain yang bersembunyi di sini juga.

@ tzickel Saya yakin kompiler terpercaya ubuntu default adalah gcc 4.8, saya tidak dapat melihat bahwa ada yang lebih baru telah diinstal.

@charris the manylinux container menjalankan Centos5 dengan gcc 4.2 terpasang.

@ngoldbaum Terima kasih atas infonya. Kami akan segera membangun roda untuk Python 3.7. Apakah itu semudah menambahkan entri MB_PYTHON_VERSION=3.7 ke matriks build travis.yml?

Saya pikir Anda harus menunggu sampai perkakas manylinux diperbarui. Itu membutuhkan waktu beberapa minggu setelah python3.6 keluar IIRC. @njmungkin tahu lebih banyak.

@ngoldbaum Apa yang mendorong pilihan gcc 4.2 ? Apakah mudah menggunakan versi kompiler yang lebih baru jika kita menginginkannya?

Apa yang mendorong pilihan gcc 4.2?

Saya yakin tujuannya adalah mengaktifkan kompilasi dengan glibc yang cukup tua sehingga masalah kompatibilitas biner tidak akan menjadi masalah dalam praktiknya.

Apakah mudah menggunakan versi kompiler yang lebih baru jika kita menginginkannya?

Saya tidak tahu. Saya juga tidak tahu bagaimana numpy mengelola roda bangunan. Saya menggunakan proyek multibuild Matthew Brett untuk proyek saya dan saya harus menunggu sampai itu diperbarui untuk membangun roda python3.7 untuk proyek saya.

Oke, tidak yakin apakah itu masalah terkait terakhir, tetapi saya rasa saya telah menemukan masalah lain dalam kode SSE itu:

Di numpy 1.14.0 beberapa kode telah ditambahkan untuk membuang peringatan runtime jika ada kesalahan FP:
https://github.com/numpy/numpy/commit/d47ca7b26172c42b01c3132d0e46e70578c8ea21

Tetapi jika kita melihat lagi implementasi SSE:
https://github.com/numpy/numpy/blob/d7d5cb3feccc1fc6cf57159e8b9fe0a733968706/numpy/core/src/umath/simd.inc.src#L1020

Kita dapat melihatnya melewati FPU hanya bagian tengah dari larik, header dan trailer (yang bukan SSEable karena tidak selaras dengan memori) secara manual diperiksa untuk NaN dan tidak melalui FPU, sedangkan SSE bagian melakukannya, sehingga hanya bagian tengah yang akan memicu peringatan NaN, sedangkan bagian lainnya (sementara ekuivalen di input dan ouput) tidak. Apa itu oke?

np.min ([1, np.nan, 1, 1, 1, 1, 1, 1]) tidak akan memicu peringatan runtime
tetapi np.min ([1, 1, np.nan, 1, 1, 1, 1, 1]) tidak.

np.min ([1, np.nan, 1, 1, 1, 1, 1, 1]) tidak akan memicu peringatan runtime

@tzickel ini terkait dengan # 11029, bukan?

Edit: pemformatan

Tampaknya masalah sumbernya adalah # 8954, yang mengarah ke PR # 9020

Ya, tetapi maksud saya adalah # 9020 tidak mencakup semua kemungkinan kasus. Salah satunya adalah kode SSE ini (yang merongrong mekanisme ini untuk beberapa pengoptimalan).

Adapun # 11029 Saya mencoba mencari tahu mengapa baik di posting di sana-sini, selain bug propogasi NaN, terkadang peringatan muncul dan terkadang tidak

Pertanyaan lain, jika NaN adalah hasil dari kedua min / max jika ada di input, dan kita sudah memeriksa isNan / tidak valid, bukankah seharusnya cepat-keluar saat menemukan contoh pertama NaN?

@tzickel tidak ada operasi pengurangan awal-keluar. Mereka mungkin di masa depan jika mereka direfraktorisasi sebagai gufunc.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat