Ecco un inizio. Innanzitutto, mi scuso per eventuali errori.
Ho sperimentato un paio di approcci diversi. Ero un po 'confuso dai limiti della somma - il limite superiore dovrebbe essere , piuttosto che i - 1 ?ioi - 1
Modifica: No, il limite superiore era corretto come indicato nella domanda. L'ho lasciato com'è qui perché un'altra risposta ora utilizza lo stesso codice, ma la correzione è semplice.
Prima una versione in 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
L'ho fatto un singolo ciclo con fette intorpidite:
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)
La versione numpy con un ciclo esplicito è circa 25 volte più veloce sul mio computer quando .n = 5000
Quindi ho scritto una versione Cython del codice (più leggibile) in loop.
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
Sul mio laptop, questo è circa 200 volte più veloce della versione in loop (e 8 volte più veloce della versione vettoriale a 1 loop). Sono sicuro che altri possono fare di meglio.
Ho giocato con una versione di Julia e sembrava (se l'ho cronometrato correttamente) paragonabile al codice Cython.