Почему numpy.sin возвращает другой результат, если размер аргумента больше 8192?

Я обнаружил, что numpy.sin ведет себя по-разному, когда размер аргумента <= 8192 и когда он> 8192. Разница заключается как в производительности, так и в возвращаемых значениях. Может кто-нибудь объяснить этот эффект?

Например, давайте посчитаем грех (пи /4):

x = np.pi*0.25
for n in range(8191, 8195):
    xx = np.repeat(x, n)
    %timeit np.sin(xx)
    print(n, np.sin(xx)[0])
64.7 µs ± 194 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
8191 0.7071067811865476
64.6 µs ± 166 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
8192 0.7071067811865476
20.1 µs ± 189 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
8193 0.7071067811865475
21.8 µs ± 13.4 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
8194 0.7071067811865475

После преодоления ограничения в 8192 элемента вычисления становятся более чем в 3 раза быстрее и дают другой результат: последняя цифра становится 5 вместо 6.

Когда я попытался вычислить то же значение другими способами, я получил:

  • C++ std::sin (Visual Studio 2017, платформа Win32) дает 0,7071067811865475;
  • C++ std::sin (Visual Studio 2017, платформа x64) дает 0,70710678118654756;
  • math.sin дает 0,7071067811865476, что логично, потому что я использовал 64-битный Python.

Я не смог найти никакого объяснения ни в документации NumPy, ни в ее коде.

Обновление № 2: Трудно поверить, но замена sin на sqrt дает следующее:

44.2 µs ± 751 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
8191 0.8862269254527579
44.1 µs ± 543 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
8192 0.8862269254527579
10.3 µs ± 105 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
8193 0.886226925452758
10.4 µs ± 4.41 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
8194 0.886226925452758

Обновление: np.show_config():

mkl_info:
    libraries = ['mkl_rt']
    library_dirs = ['C:/GNU/Anaconda3\\Library\\lib']
    define_macros = [('SCIPY_MKL_H', None), ('HAVE_CBLAS', None)]
    include_dirs = ['C:\\Program Files (x86)\\IntelSWTools\\compilers_and_libraries_2019.0.117\\windows\\mkl', 'C:\\Program Files (x86)\\IntelSWTools\\compilers_and_libraries_2019.0.117\\windows\\mkl\\include', 'C:\\Program Files (x86)\\IntelSWTools\\compilers_and_libraries_2019.0.117\\windows\\mkl\\lib', 'C:/GNU/Anaconda3\\Library\\include']
blas_mkl_info:
    libraries = ['mkl_rt']
    library_dirs = ['C:/GNU/Anaconda3\\Library\\lib']
    define_macros = [('SCIPY_MKL_H', None), ('HAVE_CBLAS', None)]
    include_dirs = ['C:\\Program Files (x86)\\IntelSWTools\\compilers_and_libraries_2019.0.117\\windows\\mkl', 'C:\\Program Files (x86)\\IntelSWTools\\compilers_and_libraries_2019.0.117\\windows\\mkl\\include', 'C:\\Program Files (x86)\\IntelSWTools\\compilers_and_libraries_2019.0.117\\windows\\mkl\\lib', 'C:/GNU/Anaconda3\\Library\\include']
blas_opt_info:
    libraries = ['mkl_rt']
    library_dirs = ['C:/GNU/Anaconda3\\Library\\lib']
    define_macros = [('SCIPY_MKL_H', None), ('HAVE_CBLAS', None)]
    include_dirs = ['C:\\Program Files (x86)\\IntelSWTools\\compilers_and_libraries_2019.0.117\\windows\\mkl', 'C:\\Program Files (x86)\\IntelSWTools\\compilers_and_libraries_2019.0.117\\windows\\mkl\\include', 'C:\\Program Files (x86)\\IntelSWTools\\compilers_and_libraries_2019.0.117\\windows\\mkl\\lib', 'C:/GNU/Anaconda3\\Library\\include']
lapack_mkl_info:
    libraries = ['mkl_rt']
    library_dirs = ['C:/GNU/Anaconda3\\Library\\lib']
    define_macros = [('SCIPY_MKL_H', None), ('HAVE_CBLAS', None)]
    include_dirs = ['C:\\Program Files (x86)\\IntelSWTools\\compilers_and_libraries_2019.0.117\\windows\\mkl', 'C:\\Program Files (x86)\\IntelSWTools\\compilers_and_libraries_2019.0.117\\windows\\mkl\\include', 'C:\\Program Files (x86)\\IntelSWTools\\compilers_and_libraries_2019.0.117\\windows\\mkl\\lib', 'C:/GNU/Anaconda3\\Library\\include']
lapack_opt_info:
    libraries = ['mkl_rt']
    library_dirs = ['C:/GNU/Anaconda3\\Library\\lib']
    define_macros = [('SCIPY_MKL_H', None), ('HAVE_CBLAS', None)]
    include_dirs = ['C:\\Program Files (x86)\\IntelSWTools\\compilers_and_libraries_2019.0.117\\windows\\mkl', 'C:\\Program Files (x86)\\IntelSWTools\\compilers_and_libraries_2019.0.117\\windows\\mkl\\include', 'C:\\Program Files (x86)\\IntelSWTools\\compilers_and_libraries_2019.0.117\\windows\\mkl\\lib', 'C:/GNU/Anaconda3\\Library\\include']

Ответ 1

Как писал @WarrenWeckesser, "это почти наверняка проблема Anaconda & Intel MKL; см. Https://github.com/numpy/numpy/issues/11448 и https://github.com/ContinuumIO/anaconda-issues/issues/9129 ".

И, к сожалению, единственный способ решить эту проблему под Windows, чтобы удалить Anaconda и использовать другой дистрибутив с MKL свободной numpy. Я использовал python-3.6.6-amd64 с https://www.python.org/ и установил все остальное через pip, включая numpy 1.14.5. Мне даже удалось заставить Spyder работать (пришлось понизить PyQt5 до 5.11.3, он отказался запускаться на> = 5.12).

Теперь np.sin(xx) постоянно составляет 0,7071067811865476 (67,1 мкс при n = 8192) и np.sqrt(xx) 0,8862269254527579 (16,4 мкс). Немного медленнее, но отлично воспроизводимо.

Beware Anaconda