Улучшение производительности сокращения с явной оценкой - PullRequest
0 голосов
/ 14 февраля 2020

Я хотел бы использовать Numberxpr для вычисления 2D-массива в виде суммы по одной оси 3D-массива. Трехмерный массив достаточно велик, чтобы его невозможно было сохранить в памяти.

Самый простой подход - использовать сокращение NumberxPR, но это довольно медленно.

import numpy as np
import numexpr as ne

numx = 1e3
xaxis = np.arange(numx) * 10 / numx
dx = xaxis[:, None] - xaxis[None, :]

qrange = np.arange(0, 20)

# naive ne implimentations
qb = qrange[None, None, :]
dxb = dx[:, :, None]
tes = "sum(1j * exp(1j * qb * dxb), axis=2)"
Gf1 = ne.evaluate(tes)

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

# avoid sum in ne, but chunk calculation to be gentle on memory
nq = 10
nes = "1j * exp(1j * qx * dxb)"
num_chunks = int(np.ceil(qrange.size / nq))
ier = np.array_split(np.arange(qrange.size), num_chunks, axis=-1)

Gf2 = np.zeros(dx.shape, dtype=np.complex)
for ix in ier:
    qx = qb[:, :, ix]
    temp = ne.evaluate(nes)
    temp = np.sum(temp, axis=-1)
    Gf2 += temp

Итеративный метод повышает производительность, но требует выполнения операций в numpy, что не так быстро, как цифра. Можно выполнить все вычисления в виде длинного явного выражения, динамически генерируя строку оценки.

# generate sum as a long string
template = np.array("1j * exp( 1j * qx * dx )".split(" "))

delt_vars = {}
bes = ""
for i, _ in enumerate(qrange):
    delt_vars['qn'+str(i)] = q
    temp = template.copy()
    temp[temp == 'qx'] = 'qn' + str(i)
    bes += '+' + ''.join(temp.tolist())

Gf3 = ne.evaluate(bes, local_dict=delt_vars)

В этом примере явное выражение для Gf3 обеспечивает наилучшую производительность. Однако переход к более сложным выражениям наталкивается на неожиданное ограничение числаxpr. Более длинные выражения ( т.е. qrange = np.arange(0, 100)) вызывают одну из двух ошибок:

ValueError: bytes must be in range(0, 256) или ValueError: too many inputs

Вторая ошибка, по-видимому, связана с ограничение numpy, обсуждаемое в: Pytables NumExpr ValueError: слишком много входных данных при запросах с большим количеством условий

Можно ли улучшить производительность сложных сокращений в Numberxpr с помощью long и явные строки оценки? Если нет, то есть ли лучший способ выполнить вычисления такого типа, которые сохранят производительность NumberxPR?

...