Cara mengekspresikan ekspresi rumit ini menggunakan irisan numpy

14

Saya ingin menerapkan ekspresi berikut dalam Python: mana x dan y adalah array numpy dengan ukuran n , dan k adalah array numpy ukuran n × n . Ukuran n mungkin hingga sekitar 10.000, dan fungsinya adalah bagian dari loop dalam yang akan dievaluasi berkali-kali, jadi kecepatan itu penting.

xsaya=j=1saya-1ksaya-j,jSebuahsaya-jSebuahj,
xynkn×nn

Idealnya saya ingin menghindari for for loop, walaupun saya kira itu bukan akhir dari dunia jika ada satu. Masalahnya adalah bahwa saya mengalami kesulitan melihat bagaimana melakukannya tanpa memiliki beberapa loop bersarang, dan itu mungkin membuatnya agak lambat.

Adakah yang bisa melihat bagaimana mengekspresikan persamaan di atas menggunakan numpy dengan cara yang efisien, dan lebih disukai juga dapat dibaca? Secara umum, apa cara terbaik untuk mendekati hal semacam ini?

Nathaniel
sumber
Saya punya pertanyaan serupa beberapa hari yang lalu. Saya menanyakannya di stackoverflow. Lihat pos ini . Saya menggunakan scipy.weave bukan cython. Adakah yang tahu jika ini membuat perbedaan kinerja (besar)?
seb

Jawaban:

17

Inilah solusi Numba. Di komputer saya, versi Numba> 1000x lebih cepat dari versi python tanpa dekorator (untuk matriks 200x200, 'k' dan vektor panjang 200 'a'). Anda juga dapat menggunakan dekorator @autojit yang menambahkan sekitar 10 mikrodetik per panggilan sehingga kode yang sama akan bekerja dengan beberapa tipe.

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

Pengungkapan: Saya adalah salah satu pengembang Numba.

Travis Oliphant
sumber
Terima kasih, itu terlihat sangat mudah. Aku bahkan tidak tahu tentang numba! Cython, PyPy, Numba ... ini adalah dunia yang membingungkan.
Nathaniel
3
Travis, sangat keren, apakah Anda keberatan menambahkan pengungkapan ke bagian bawah jawaban Anda bahwa Anda adalah salah satu pengembang numba?
Aron Ahmadia
1
Dengan , versi Cython juga relatif lebih cepat dibandingkan dengan Python yang dilingkarkan (~ 700x, untuk saya). Saya ingin tahu tentang bagaimana perubahan kinerja ini dengan matriks yang lebih besar, dan apakah mereka mengalami hambatan (memori?) Yang sama. n=200
Nat Wilson
@NatWilson - jika Anda menanyakan hal ini pada scicomp, saya akan dengan senang hati mencoba dan mengatasinya untuk Anda :)
Aron Ahmadia
4

Ini awal. Pertama, permintaan maaf saya untuk kesalahan.

sayasaya-1

Sunting: Tidak, batas atas benar seperti yang disediakan dalam pertanyaan. Saya membiarkannya seperti di sini karena jawaban lain sekarang menggunakan kode yang sama, tetapi perbaikannya sederhana.

Pertama versi berulang:

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

Saya membuatnya menjadi satu loop dengan irisan numpy:

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)

n=5000

Lalu saya menulis versi loop dari kode (lebih mudah dibaca) 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

Di laptop saya, yang ini sekitar 200x lebih cepat dari versi looped (dan 8x lebih cepat dari versi vektor 1-loop). Saya yakin orang lain bisa berbuat lebih baik.

Saya bermain dengan versi Julia, dan sepertinya (jika saya mengatur waktunya dengan benar) sebanding dengan kode Cython.

Nat Wilson
sumber
x0saya-1
Ah, begitu. Saya mengumpulkan itu dari penjumlahan asli, tetapi tidak yakin itu maksudnya.
Nat Wilson
1

Apa yang Anda inginkan tampaknya menjadi lilitan; Saya pikir cara tercepat untuk mencapainya adalah numpy.convolvefungsinya.

Anda mungkin harus memperbaiki indeks sesuai dengan kebutuhan Anda, tetapi saya pikir Anda ingin mencoba sesuatu seperti:

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

result = np.convolve(a, k*a[::-1])
Thomas Baruchel
sumber