Numpy: ОШИБКА: np.min не всегда распространяет NaN

Созданный на 11 янв. 2018  ·  65Комментарии  ·  Источник: numpy/numpy

Это расследование возникло из-за того, что scipy CI начал давать сбой на appveyor в последние несколько дней, то есть после выпуска 1.14.

На моем домашнем компьютере (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

На моем рабочем компьютере (macOS, conda python 3.6.2, numpy установлен через pip в чистом окружении):

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

С первым набором примеров кода, почему не первые три примера приводят к предупреждению, а только последнее?
Во втором наборе примеров предупреждение вообще не выводится.

00 - Bug numpy.core

Все 65 Комментарий

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

Я думаю, что еще один момент здесь заключается в том, что это могут быть разные пути кода, которые вызывают различное (не совсем непонятное, как мне кажется) поведение, например, из-за использования AVX или чего-то подобного, я думаю, что иногда видел, что этот векторный материал не мог установить флаги ошибок процессора, и поскольку это может показаться немного случайным, если они используются или нет ....

Не знаю, имеет ли это какое-то отношение к этому, но я ожидаю этого, numpy обычно не делает слишком много с ошибками с плавающей запятой. Кроме проверки, случилось ли что-нибудь.

И, как сказал Чак, к сожалению, во многих случаях этот флаг ошибки с плавающей запятой отличается в разных системах.

Хорошо, понял, почему ошибка возникает только при некоторых установках, а не на других.
Причина, по которой scipy использует np.min в массиве, который, возможно, содержит NaN, заключается в том, что это быстрый способ проверки их наличия. Документация numpy предполагает, что это допустимо:

Значения NaN распространяются, то есть, если хотя бы один элемент равен NaN, соответствующее минимальное значение также будет NaN.

Учитывая, что это один из допустимых вариантов использования np.min, с точки зрения использования я бы не ожидал появления каких-либо предупреждений / ошибок вообще.

(Очевидно, есть и другие способы достижения этого, например np.isnan(arr).any() , но это не меняет моих рассуждений выше)

Думаю, это совсем не правдоподобное изменение.

+1 за отсутствие предупреждений

Привет,

Я наткнулся на похожее несоответствие:

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

Почему фигура более 8 вызывает это предупреждение RuntimeWarning?

@NotSqrt @seberg Я наблюдаю аналогичное поведение, когда min вообще не распространяет NaN правильно, когда размер входного массива достигает 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])

Демонстрирует это довольно странное поведение на OSX и Windows, но не на Linux .. свежий virtualenv с использованием python 2.7.13 и 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

Видите там две строки «это должно быть nan согласно docs: 19.0 »?

Также nb предупреждение не появляется в numpy 1.13.1 (где я впервые заметил это поведение).

@kippr Где ты

Я собираюсь предположить, что граница из восьми элементов может быть связана с AVX512, и проблема заключается в некоторой комбинации компилятора и процессора. На каком процессоре / компиляторе вы наблюдаете проблему?

@juliantaylor Мысли?

Выравнивание также может иметь эффект.

Привет @charris

Спасибо, что посмотрели мой пост .. чтобы ответить на ваши вопросы:

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

NumPy был установлен через pip на моем Mac в новом virtualenv (python 2.7.13, установленный через homebrew), поэтому я предполагаю, что он использует все флаги компилятора по умолчанию и т.д.?

Я только что воссоздал виртуальный env и повторно запустил установку pip, и вот что мне кажется уместным, но при установке появляется много сообщений (полный журнал прилагается). Сообщите мне, есть ли у меня другая информация. может выудить из каталога сборки или чего-то еще, или если есть флаги компиляции, чтобы попробовать.

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

благодаря
Крис

build.log

Ps, наверное, неудивительно, но такое же поведение проявляется с 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

Я использую ту же версию numpy (на Arch) с процессором, который также имеет возможности AVX2 и sse4.2 и не вижу его в Linux. Может это как-то связано с mac / clang?

РЕДАКТИРОВАТЬ / Поправка: (я немного пробовал провоцировать разные выравнивания, не проверял часть предупреждения, но неправильную часть результата я не вижу)

Я предполагаю, что есть флаг процессора, который не устанавливается правильно, вероятно, связанный с компилятором.

У нас должен быть тест, который выявит эту проблему, хотя бы для ее отслеживания. @kippr Что значит

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

делать на своей установке?

Привет @charris

Вроде нормально:

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

Но я попробовал еще несколько комбинаций и нашел следующее (см. Последний, 8 значений и ось = 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])

Довольно загадочно ... не уверен, что это вообще помогает понять причину.

@seberg - да, правильно, я наблюдал такое поведение на своих Mac, а также в Windows, но с Linux все было в порядке.

благодаря
Крис

@kippr Это действительно тревожная ошибка, поскольку она влияет на базовую операцию, и проблема, вероятно, не связана с NumPy. Мы можем попробовать отключить векторизацию на MAC и Windows и посмотреть, поможет ли это. Вы видите эту проблему только на Python 2.7?

Я не вижу ничего в NumPy, что изменило бы это поведение для 1.14. Может быть, OpenBLAS возится с элементами управления ...

@juliantaylor @VictorRodriguez Мысли?

Привет @charris

Я вижу такое поведение также в python 3 на моем 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, я впервые заметил такое поведение в NumPy версии 1.13 под python 2.7 на моем Mac, так что это не было регрессией, представленной в 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'

И воспроизвел его в 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'

Но не вижу этой проблемы в 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 Я видел в своем путешествии по оптимизации производительности для numpy, что флаги openblas и компилятора могут влиять на поведение numpy / scipy (на основе моего опыта). Первый шаг, который мы могли бы сделать, - это исправить это в тесте C (с использованием библиотек openblas), чтобы мы могли изолировать поведение и посмотреть, можно ли его воспроизвести. Также просто дважды проверьте, что это только на MAC и Windows, верно? Я не вижу этого поведения в Linux (теперь патчи avx входят в 1.15, поэтому они не должны влиять на это). @kippr как вы

Привет @VictorRodriguez

Это было через установку pip на обеих платформах, новый virtualenv в случае Mac. См. Выше, где я вставил журнал для pip, включая вывод компиляции для установки python 2.7. (Установка Python 3 кажется колесом.)

Ура Крис

Привет народ
Есть ли дополнительная информация, которую я могу предоставить, чтобы помочь?

благодаря

На Mac (и, возможно, на других платформах) ошибка возникает из-за того, что компилятор (на Mac по умолчанию clang / llvm не gcc) переупорядочивает команды SSE2 проблемным образом. Ошибка в:

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

происходит, потому что выполняемый код - это sse2_minimum_DOUBLE, сгенерированный из:
https://github.com/numpy/numpy/blob/d7d5cb3feccc1fc6cf57159e8b9fe0a733968706/numpy/core/src/umath/simd.inc.src#L1020

В частности, давайте посмотрим на этот код (я автоматически расширил форму SSE2) внутри функции в строке 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);
            ....
         }

Компилятор не понимает, что присвоение c1 и npy_get_floatstatus () связаны, и поэтому изменяет код на:

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

что, конечно, бессмысленно ... Я не знаю, какой рекомендуемый способ заставить его не делать этого при оптимизации. (Я думаю, что на других платформах для этого есть прагма STDC FENV_ACCESS?)

Может быть, поскольку изменениям в этом коде 5 лет, ошибка вызвана новой версией clang и другими оптимизациями?

Спасибо @tzickel

Я не писал C со времен средней школы много месяцев назад, поэтому я определенно не собираюсь выяснять умный способ решить эту проблему, но я попробовал несколько вещей, чтобы заставить компилятор выполнять оценку в исходном порядке с помощью изменение оператора if из приведенного выше на:

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

И действительно, как только я это сделаю, ошибка исчезнет.

(Я также пробовал кучу других менее глупых форм, чтобы попытаться заставить компилятор оценивать _mm_min_pd перед оператором if, например, помещая ссылки на c1 с обеих сторон оператора if и после блока if / else, все для безрезультатно. Я подозреваю, что он либо их переупорядочил, но всегда выполнял назначение c = _mm_min_pd, либо решил, что мои вызовы на самом деле были нупами той или иной формы ..)

Но в любом случае я могу подтвердить, что вы определили ошибку, которую я вижу. Будем надеяться, что у кого-то есть хороший способ намекнуть компилятору оставить порядок _mm_min_pd и npy_get_floatstatus в покое ..

Хорошо, еще одна вещь, которую Google предложил, которая также сработала: маркировка c1 volatile:

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

становится:

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

Но я не уверен, что это означает, что это лучший способ достичь цели.

Большое спасибо за дополнительную отладку на этом @tzickel, это была очень глубокая отладка, @kippr У меня есть вопрос, вы упомянули, что это новая установка pip на Mac, так что не предполагается сборка с нуля, верно? Также, если я запускал свою систему Linux с gcc:

$ питон
Python 3.6.5 (по умолчанию, 1 апреля 2018 г., 15:40:54)
[GCC 7.3.0] в Linux
Для получения дополнительной информации введите «помощь», «авторские права», «кредиты» или «лицензия».

импортировать numpy как np
np.version.version
"1.14.2"
np.min ([1., 2., 3., 4., np.nan])
нан
np.min ([1., 2., 3., np.nan, 4.])
нан
np.min ([1., 2., np.nan, 3., 4.])
нан
np.min ([1., np.nan, 2., 3., 4.])
нан
np.min ([np.nan, 1., 2., 3., 4.])
нан
np.min ([np.nan, 1.])
Нан
np.min ([np.nan, 1., np.nan])
нан
np.min ([1., np.nan])
Нан
np.seterr (all = 'поднять')
{'делить': 'предупреждать', 'больше': 'предупреждать', 'ниже': 'игнорировать', 'недействительно': 'предупреждать'}
np.min ([np.nan, 1.0])
Нан

и не видите никаких предупреждений, возможно, как описано, проблема может быть в лязгах. единственное, что заставляет меня задаться вопросом, почему вы видите это с помощью pip install numpy. Как это было построено? а какие флаги использовались?

Также нет ошибки с моим Mac:

https://hastebin.com/cuzinajero.swift

Интересно, можно ли сделать это безопаснее с помощью чего-то вроде

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

@juliantaylor Мысли?

Или даже

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

Предполагается, что оператор запятой является точкой последовательности, хотя он может быть небезопасным для потоков. Хм, интересно , сколько в SIMD коде потокобезопасен.

@VictorRodriguez, если я запустил pip install numpy на моем Mac с python2.7, он будет построен из источника. Если я запускаю pip install с python3, он устанавливается с колеса.

Я записал полный вывод установки pip: https://github.com/numpy/numpy/files/1912086/build.log

Чтобы проверить приведенные выше изменения, я создал исходный код (текущий мастер), но проверил, что без настройки я все еще наблюдал некорректное поведение. При его создании я использовал командную строку, предложенную в INSTALL.rst.txt:
python setup.py build -j 4 install --prefix $HOME/.local

Предупреждение: кто-то здесь говорит, что видел ошибку и в окнах. Я предполагаю, что он не был скомпилирован с помощью clang в Windows, но с MSVC или GCC это может означать, что numpy не соблюдает правила платформы. В Windows numpy использует _statusfp для получения состояния fp, в документации MSDN указано:

Многие математические библиотечные функции изменяют слово состояния с плавающей запятой, что приводит к непредсказуемым результатам. Оптимизация может переупорядочивать, комбинировать и исключать операции с плавающей запятой вокруг вызовов _status87, _statusfp и связанных функций. Используйте параметр компилятора / Od (Disable (Debug)) или директиву pragma fenv_access, чтобы предотвратить оптимизацию, меняющую порядок операций с плавающей запятой.

GCC и MSVC (но пока не clang) имеют прагму для управления этим.

@tzickel уверен, что сборка Windows сказала, что это было с cygwin. У меня он не воспроизводится с MSVC.

в целом мне нравится предложение @charris , это стандартный C, не требует прагм, а AFAICT не добавляет никаких накладных расходов

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

который поддерживается даже компилятором Visual Studio 8, используемым для Python 2.7.
второе, более компактное предложение кажется слишком запутанным

Есть ли другие места в коде, подверженные этой ошибке?

  1. Сборка cygwin говорит о GCC, что означает, что ошибка должна действовать как linux, нет?

  2. Если я правильно понял, оператор запятой не помогает с оптимизацией, он работает на другом уровне генерации кода. Компилятор по-прежнему может думать, что оба выражения не связаны между собой, и перемещать их. Вот небольшой пример, чтобы показать, что это так (попробуйте изменить код комментариев со строкой рядом с ним), проверьте minpd относительно вызова внешней функции (убедитесь, что задано -O3 в флагах компилятора) и переключитесь между GCC и CLANG (похоже, что у ствола GCC сейчас такая же ошибка :)):

https://godbolt.org/g/Zoc5xr

  1. Если вы посмотрите на fenv.h, в основном каждая функция в коде, которая обращается к этому файлу, имеет одинаковый потенциал для проблем. Итак, все, что вызывается в коде из этого, используется в numpy / core / src / npymath / ieee754.c

  2. На мой взгляд, clang в настоящее время не может создавать безопасный код для этого типа функций с оптимизацией, поэтому варианты:
    A. Скомпилируйте с помощью gcc (по крайней мере, официальные колеса Mac) и выдайте предупреждение при компиляции с помощью clang.
    B. Clang поддерживает атрибут optnone для каждой функции, который можно добавить к таким функциям, чтобы отключить все оптимизации (более медленный, но правильный код), если он скомпилирован с clang:
    https://clang.llvm.org/docs/AttributeReference.html#optnone -clang-optnone-clang-optnone

Переключение на оператор запятой совершенно не поможет - ; уже является точкой последовательности.

@ Эрик-Визер Вы уверены? Я не вижу ; списке как точка последовательности. https://msdn.microsoft.com/en-us/library/azk8zbxd.aspx .

В любом случае порядок, в котором оцениваются аргументы оператора запятой, гарантирован, чего нельзя сказать о операторах, разделенных ; ,

@charris, к сожалению, предлагаемое изменение, похоже, не

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, отметка переменной как изменчивой действительно решает проблему.

Дайте мне знать, если вы хотите попробовать другие варианты.

Кажется, что компиляторы не придерживаются спецификации относительно операторов запятой или, по крайней мере, как мы ее понимаем. Самым простым решением для текущего состояния компиляторов кажется добавление volatile , хотя это кажется хрупким.

Что ж, GCC 8.1 только что был выпущен, и угадайте, что ... GCC с оптимизацией теперь вызывает ту же проблему, что и clang https://github.com/numpy/numpy/issues/10370#issuecomment -384154230 (а запятая не помогает там, как и ожидалось, но volatile делает), хотя я не знаю, включает ли distutils флаги gcc, которые смягчают это, я бы не подумал.

Вот код GCC 8.1 (вы можете сравнить с 7.3, где все в порядке):
https://godbolt.org/g/AJRdRQ

Найдите там вызовы minpd и npy_get_floatstatus в asm.

Давайте пока перейдем к volatile вместе с некоторыми тестами, которые, будем надеяться, должны сигнализировать о проблеме, если она возникнет. Другой вариант, который мы могли бы рассмотреть, - это отдельная функция, в которой мы могли бы использовать volatile или директивы компилятора, но хранить ее в одном месте, чтобы упростить управление.

Это гораздо более серьезный случай, чем эта конкретная проблема. Весь numpy-код, вызывающий функции в ieee754.c, необходимо проверить и исправить. (Возможно, переименуйте этот выпуск еще раз, а еще лучше - откройте новый выпуск).

Неожиданное поведение инструкции minpd может иметь отношение к https://github.com/numpy/numpy/issues/10370#issuecomment -381241813:

Если только одно значение является NaN (SNaN или QNaN) для этой инструкции, второй операнд (исходный операнд), либо NaN, либо действительное значение с плавающей запятой, записывается в результат.

См .: http://www.felixcloutier.com/x86/MINPD.html

@mattkretz не похоже. (это ожидаемое поведение и почему эта проверка уже существует).

@tzickel Из любопытства, на каком уровне оптимизации возникает проблема? Раньше мы ограничивались -O2 , но я думаю, что некоторые платформы использовали -O3 или эквивалент.

Я думаю, что похоже на эту ошибку gcc:

Похоже, что установка #pragma STDC FENV_ACCESS ON - правильное решение здесь, но для этого требуется C99, для использования которого нам нужно отказаться от python 2.7. Возможно, режим GCC --std=c90 предполагает, что он может выполнить оптимизацию из-за отсутствия прагмы

По всем вопросам просто переходите по ссылке здесь:

https://godbolt.org/g/AJRdRQ (* похоже, общий доступ, поэтому, возможно, скопируйте ввод в новое окно)

и попробуйте изменить версию компилятора, флаги компилятора и код и увидеть результат на лету ...

Это происходит при использовании даже -O1 в обоих последних компиляторах. #Pragma, похоже, ничего не делает ....

Эта версия очень сложна, в ней слишком много ассемблера, и трудно связать ассемблер с кодом на языке C. Первоначальная версия была намного проще и также продемонстрировала проблему.

Смотрите также PR

хм .... может ли кто-нибудь сказать мне, на какой ОС / компиляторе был скомпилирован numpy-1.14.3-cp27-cp27mu-manylinux1_x86_64.whl (из архива pypi)? Я думаю, здесь может скрываться еще одна (связанная) проблема.

@tzickel Я считаю, что надежный компилятор ubuntu по умолчанию - gcc 4.8, я не вижу, чтобы было установлено что-то более свежее.

@charris контейнер manylinux запускает Centos5 с установленным gcc 4.2.

@ngoldbaum Спасибо за информацию. Скоро нам нужно будет создать колеса для Python 3.7. Будет ли это так же просто, как добавить запись MB_PYTHON_VERSION=3.7 в матрицу сборки travis.yml?

Я думаю, вам нужно подождать, пока не обновятся инструменты manylinux. Это заняло несколько недель после выхода python3.6 IIRC. @njsmith, вероятно, знает больше.

@ngoldbaum Что привело к выбору gcc 4.2 ? Легко ли использовать более позднюю версию компилятора, если мы того пожелаем?

Что побудило выбор gcc 4.2?

Я считаю, что цель состояла в том, чтобы включить компиляцию с достаточно старой glibc, чтобы проблемы с двоичной совместимостью не были проблемой на практике

Легко ли использовать более позднюю версию компилятора, если мы того пожелаем?

Я не знаю. Я также не знаю, как numpy управляет строительными колесами. Я использую проект multibuild Мэтью Бретта для своих проектов, и мне нужно подождать, пока он обновится, чтобы построить колеса python3.7 для моих проектов.

Хорошо, не уверен, что это последняя проблема, но я думаю, что нашел другую проблему в этом коде SSE:

В numpy 1.14.0 был добавлен код для выдачи предупреждений во время выполнения, если есть ошибки FP:
https://github.com/numpy/numpy/commit/d47ca7b26172c42b01c3132d0e46e70578c8ea21

Но если мы снова посмотрим на реализацию SSE:
https://github.com/numpy/numpy/blob/d7d5cb3feccc1fc6cf57159e8b9fe0a733968706/numpy/core/src/umath/simd.inc.src#L1020

Мы можем видеть, что он проходит через FPU только выровненную по середине часть массива, заголовок и трейлер (которые не являются SSEable, потому что они не выровнены по памяти) вручную проверяются на NaN и не проходят через FPU, в то время как SSE части делают, поэтому только средняя часть вызовет предупреждение NaN, а другая (хотя и эквивалентна во входных и выходных) нет. Это нормально ?

np.min ([1, np.nan, 1, 1, 1, 1, 1, 1]) не вызывает предупреждения во время выполнения
но np.min ([1, 1, np.nan, 1, 1, 1, 1, 1]) делает.

np.min ([1, np.nan, 1, 1, 1, 1, 1, 1]) не вызывает предупреждения во время выполнения

@tzickel, это связано с # 11029, верно?

Изменить: форматирование

Похоже, проблема с источником была # 8954, что привело к PR # 9020.

Да, но я хочу сказать, что №9020 не охватил все возможные случаи. Один из них - это SSE-код (который подрывает этот механизм для некоторой оптимизации).

Что касается # 11029, я пытаюсь понять, почему и в сообщениях здесь, и там, помимо ошибки распространения NaN, иногда появляются предупреждения, а иногда нет.

Другой вопрос, если NaN является результатом как min / max, если он существует во входных данных, и мы уже проверяем isNan / invalid, не следует ли быстро выходить после обнаружения первого экземпляра NaN?

@tzickel нет операций сокращения до выхода. Возможно, в будущем они будут преобразованы в gufuncs.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги