Ошибка Sympy/mpmath/gmpy при использовании многопроцессорной обработки

РЕДАКТИРОВАТЬ: это симпатичная ошибка. Я переместил обсуждение на https://github.com/sympy/sympy/issues/7457

У меня есть программа на Python, которая использует sympy для выполнения некоторых основных функций, связанных с пересечением линии и формы. Эту операцию необходимо выполнить несколько тысяч раз, и она довольно медленная при использовании sympy чистых модулей Python по умолчанию.

Я попытался ускорить это, установив gmpy 2.0.3 (я также пробовал с gmpy 1.5). Это приводит к некоторому ускорению кода, но при использовании multiprocessing для дальнейшего ускорения программа вылетает с ошибкой TypeError.

Exception in thread Thread-3:
Traceback (most recent call last):
  File "C:\python27\lib\threading.py", line 810, in __bootstrap_inner
    self.run()
  File "C:\python27\lib\threading.py", line 763, in run
    self.__target(*self.__args, **self.__kwargs)
  File "C:\python27\lib\multiprocessing\pool.py", line 376, in _handle_results
    task = get()
  File "C:\python27\lib\site-packages\sympy\geometry\point.py", line 91, in __new__
    for f in coords.atoms(Float)]))
  File "C:\python27\lib\site-packages\sympy\simplify\simplify.py", line 3839, in nsimplify
    return _real_to_rational(expr, tolerance)
  File "C:\python27\lib\site-packages\sympy\simplify\simplify.py", line 3781, in _real_to_rational
    r = nsimplify(float, rational=False)
  File "C:\python27\lib\site-packages\sympy\simplify\simplify.py", line 3861, in nsimplify
    exprval = expr.evalf(prec, chop=True)
  File "C:\python27\lib\site-packages\sympy\core\evalf.py", line 1300, in evalf
    re = C.Float._new(re, p)
  File "C:\python27\lib\site-packages\sympy\core\numbers.py", line 673, in _new
    obj._mpf_ = mpf_norm(_mpf_, _prec)
  File "C:\python27\lib\site-packages\sympy\core\numbers.py", line 56, in mpf_norm
    rv = mpf_normalize(sign, man, expt, bc, prec, rnd)
TypeError: ('argument is not an mpz', <class 'sympy.geometry.point.Point'>, (-7.07106781186548, -7.07106781186548))

Программа отлично работает при запуске в одном процессе с использованием gmpy и при запуске без gmpy с использованием multiprocessing.Pool.

Кто-нибудь сталкивался с такой проблемой раньше? Программа ниже воспроизводит эту проблему:

import sympy
import multiprocessing
import numpy

def thread_function(func, data, output_progress=True, extra_kwargs=None, num_procs=None):
    if extra_kwargs:
        func = functools.partial(func, **extra_kwargs)

    if not num_procs:
        num_procs = multiprocessing.cpu_count()
    pool = multiprocessing.Pool(processes=num_procs)
    results = pool.map_async(func, data.T)
    pool.close()

    pool.join()
    return results.get()

def test_fn(data):
    x = data[0]
    y = data[1]
    circle = sympy.Circle((0,0), 10)
    line = sympy.Line(sympy.Point(0,0), sympy.Point(x,y))
    return line.intersection(circle)[0].evalf()

if __name__ == '__main__':
    data = numpy.vstack((numpy.arange(1, 100), numpy.arange(1, 100)))

    print thread_function(test_fn, data) #<--- this line causes the problem
#    print [test_fn(data[:,i]) for i in xrange(data.shape[1])] #<--- this one runs without errors

person Chinmay Kanchi    schedule 02.05.2014    source источник
comment
Разве многопроцессорность не использует pickle? Возможно, объекты gmpy не поддаются обработке.   -  person asmeurer    schedule 03.05.2014


Ответы (1)


Я убедился, что объекты gmpy можно выбрать и что объекты mpmath.mpf, использующие gmpy, также можно выбрать.

Ошибка возникает, когда аргумент man для mpf_normalize() не является объектом gmpy. Если я заставлю man быть mpz, я больше не получаю ошибку. Но ответ отличается от версии с одним процессом.

Версия с одним процессом:

Точка(-223606797749979/50000000000000, -223606797749979/25000000000000)

Множественная версия процесса:

Точка(-7,07106781186548, -7,07106781186548)

Оба типа, используемые в Point(), различны (рациональный и плавающий), и значения разные (-223606797749979/50000000000000 равно -4,47213595499958).

Я все еще изучаю и обновлю этот ответ, если обнаружу основную причину.

Обновление №1. Различие в значениях вызвано ошибкой в ​​примере кода. Поточной функции были переданы значения, отличные от непоточной версии.

Я все еще отслеживаю, почему многопроцессорность вызывает исключение. Я сократил проблему до следующего примера:

import sympy
import multiprocessing
import numpy

def thread_function(func, data, output_progress=True, extra_kwargs=None, num_procs=None):
    if extra_kwargs:
        func = functools.partial(func, **extra_kwargs)

    if not num_procs:
        num_procs = multiprocessing.cpu_count()
    pool = multiprocessing.Pool(processes=num_procs)
    results = pool.map_async(func, data)
    pool.close()

    pool.join()
    return results.get()

def test_fn(data):
    return sympy.Point(0,1).evalf()

if __name__ == '__main__':
    test_size = 10
    print [test_fn(None) for i in xrange(1, test_size)] #<--- this one runs without errors
    print thread_function(test_fn, [None] * (test_size - 1)) #<--- this line causes the problem
person casevh    schedule 04.05.2014
comment
Ошибка в примере кода заключается в том, что данные повторяются по столбцам в примере с многопроцессорной обработкой. Я исправил это в вопросе. - person Chinmay Kanchi; 04.05.2014
comment
Обратите внимание, что, поскольку это симпатичная ошибка, я собираюсь перенести обсуждение туда. См. github.com/sympy/sympy/issues/7457. - person Chinmay Kanchi; 04.05.2014