Como expressar essa expressão complicada usando fatias numpy


14

Desejo implementar o seguinte expressão em Python: onde x e y são matrizes numpy de tamanho n , e k é uma matriz numpy de tamanho n × n . O tamanho n pode ser de até 10000, e a função faz parte de um loop interno que será avaliado muitas vezes, portanto a velocidade é importante.

xEu=j=1Eu-1kEu-j,jumaEu-jumaj,
xynkn×nn

Idealmente, eu gostaria de evitar um loop for completamente, embora eu ache que não é o fim do mundo, se houver um. O problema é que estou tendo problemas para ver como fazer isso sem ter alguns loops aninhados, e isso provavelmente tornará o processo bastante lento.

Alguém pode ver como expressar a equação acima usando numpy de maneira eficiente e, de preferência, também legível? De maneira mais geral, qual é a melhor maneira de abordar esse tipo de coisa?


Eu tive uma pergunta semelhante há alguns dias. Eu perguntei no stackoverflow. Confira este post . Eu uso scipy.weave em vez de cython. Alguém sabe se isso faz alguma diferença (considerável) de desempenho?
seb

Respostas:


17

Aqui está a solução Numba. Na minha máquina, a versão Numba é> 1000x mais rápida que a versão python sem o decorador (para uma matriz de 200x200, 'k' e vetor de comprimento 200 'a'). Você também pode usar o decorador @autojit, que adiciona cerca de 10 microssegundos por chamada, para que o mesmo código funcione com vários tipos.

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

Divulgação: Sou um dos desenvolvedores do Numba.


Obrigado, isso parece bem direto. Eu nem sabia sobre numba! Cython, PyPy, Numba ... é um mundo confuso.
Nathaniel

3
Travis, muito legal, você se importa de adicionar uma divulgação ao final da sua resposta de que você é um dos desenvolvedores da numba?
Aron Ahmadia 9/03/2013

1
Com , a versão do Cython também é muito mais rápida em relação ao Python em loop (~ 700x, para mim). Gostaria de saber como esse desempenho muda com matrizes maiores e se elas apresentam os mesmos gargalos (memória?). n=200
Nat Wilson

@NatWilson - se você perguntar isso como uma pergunta sobre scicomp, eu ficaria feliz de tentar resolvê-lo para você :)
Aron Ahmadia

4

Aqui está um começo. Primeiro, peço desculpas por qualquer erro.

EuEu-1

Edit: Não, o limite superior estava correto conforme fornecido na pergunta. Deixei como está aqui, porque outra resposta agora usa o mesmo código, mas a correção é simples.

Primeiro uma versão em loop:

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

Eu fiz um único loop com fatias 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

Então eu escrevi uma versão Cython do código em loop (mais legível).

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

No meu laptop, este é cerca de 200x mais rápido que a versão em loop (e 8x mais rápido que a versão vetorizada em 1-loop). Tenho certeza que outros podem fazer melhor.

Eu joguei com uma versão Julia, e parecia (se eu cronometrasse corretamente) comparável ao código Cython.


x0 0Eu-1

Ah entendo. Reuni isso no resumo original, mas não tinha certeza de que era essa a intenção.
Nat Wilson

1

O que você quer parece ser uma convolução; Eu acho que a maneira mais rápida de conseguir isso seria a numpy.convolvefunção.

Talvez você precise corrigir os índices de acordo com suas necessidades exatas, mas acho que você gostaria de tentar algo como:

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

result = np.convolve(a, k*a[::-1])
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.