Як висловити цей складний вираз, використовуючи нумеровані фрагменти


14

Я хочу реалізувати наступний вираз у Python: де x і y - нумерові масиви розміром n , а k - масивний масив розміру n × n . Розмір n може становити приблизно 10000, а функція є частиною внутрішнього циклу, який буде оцінюватися багато разів, тому важлива швидкість.

хi=j=1i-1кi-j,jаi-jаj,
хункн×нн

В ідеалі я хотів би взагалі уникнути циклу for, хоча я думаю, це не кінець світу, якщо він є. Проблема полягає в тому, що у мене виникають проблеми, як це зробити, не маючи пари вкладених циклів, і це, швидше за все, зробить це досить повільно.

Чи може хтось бачити, як виразити вищевказане рівняння, використовуючи numpy, таким чином, що є ефективним і, бажано, також читабельним? Більш загально, який найкращий спосіб підійти до такого роду речей?


У мене було подібне питання пару днів тому. Я запитав це над stackoverflow. Перевірте цю публікацію . Я використовую scipy.weave замість цитону. Хтось знає, чи є це різницею у роботі?
seb

Відповіді:


17

Ось рішення Numba. На моїй машині версія Numba на 1000 разів швидша, ніж версія python без декоратора (для матриці 200x200, 'k' та вектора довжини 200 'a'). Ви також можете використовувати декоратор @autojit, який додає приблизно 10 мікросекунд на виклик, щоб той самий код працював з декількома типами.

from numba import jit, autojit

@jit('f8[:](f8[:,:],f8[:])')
#@autojit
def looped_ver(k, a):
    x = np.empty_like(a)
    for i in range(x.size):
        sm = 0.0
        for j in range(0, i+1):
            sm += k[i-j,j] * a[i-j] * a[j]
        x[i] = sm
    return x

Розкриття інформації: Я один із розробників Numba.


Дякую, це виглядає досить просто. Я навіть не знав про numba! Cython, PyPy, Numba ... це заплутаний світ.
Натаніел

3
Тревіс, дуже класно, чи не проти додати розкриття внизу своєї відповіді, що ти один із розробників numba?
Арон Ахмадія

1
н=200

@NatWilson - якщо ви ставите це як питання на scicomp, я би радий спробувати вирішити це для вас :)
Aron Ahmadia

4

Ось початок. По-перше, мої вибачення за будь-які помилки.

ii-1

Редагувати: Ні, верхня межа була правильною, як зазначено в питанні. Я залишив це як тут, оскільки інша відповідь зараз використовує той самий код, але виправлення просте.

Спочатку циклічна версія:

def looped_ver(k, a):
    x = np.empty_like(a)
    for i in range(x.size):
        sm = 0
        for j in range(0, i+1):
            sm += k[i-j,j] * a[i-j] * a[j]
        x[i] = sm
    return x

Я зробив це єдиною петлею з нумистими скибочками:

def vectorized_ver(k, a):
    ktr = zeros_like(k)
    ar = zeros_like(k)
    sz = len(a)
    for i in range(sz):
        ktr[i,:i+1] = k[::-1].diagonal(-sz+i+1)
        a_ = a[:i+1]
        ar[i,:i+1] = a_[::-1] * a_
    return np.sum(ktr * ar, 1)

Коли на моєму комп’ютері версія nummy з одним явним циклом на 25 разів швидша н=5000.

Тоді я написав Cython версію (більш читабельного) петельного коду.

import numpy as np
import cython
cimport numpy as np

@cython.boundscheck(False)
@cython.wraparound(False)
def cyth_ver(double [:, ::1] k not None,
              double [:] a not None):
    cdef double[:] x = np.empty_like(a)
    cdef double sm
    cdef int i, j

    for i in range(len(a)):
        sm = 0.0
        for j in range(i+1):
            sm = sm + k[i-j,j] * a[i-j] * a[j]
        x[i] = sm
    return x

На моєму ноутбуці цей апарат приблизно в 200 разів швидший за циклічну версію (і на 8 разів швидший, ніж векторизована версія з 1 циклом). Я впевнений, що інші можуть зробити краще.

Я грав із версією Julia, і здавалося, що (якщо я її приурочив належним чином) порівняно з кодом Cython.


Велике дякую. Це переконало мене, що мені потрібно докласти зусиль, щоб засвоїти Cython :) Я повинен був це сказатих0 завжди має бути нуль, тому що індекс збирається i-1значить немає термінів для підсумовування - але це незначна деталь.
Натаніель

А, бачу. Я зібрав це з оригінальної підсумки, але не був впевнений, що це було наміром.
Нат Вілсон

1

Те, що ви хочете, здається, є згортком; Я думаю, що найшвидшим способом її досягнення стала б numpy.convolveфункція.

Можливо, вам доведеться фіксувати індекси відповідно до ваших точних потреб, але я думаю, ви хочете спробувати щось на кшталт:

import numpy as np
a = [1, 2, 3, 4, 5]
k = [2, 4, 6, 8, 10]

result = np.convolve(a, k*a[::-1])
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.