Voici un début. Tout d'abord, mes excuses pour toute erreur.
J'ai expérimenté quelques approches différentes. J'étais un peu confus par les limites de la somme - la limite supérieure devrait-elle être , plutôt que i - 1 ?jei - 1
Edit: Non, la limite supérieure était correcte comme indiqué dans la question. Je l'ai laissé tel quel, car une autre réponse utilise maintenant le même code, mais la correction est simple.
D'abord une version en boucle:
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
Je l'ai fait une seule boucle avec des tranches 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)
La version numpy avec une boucle explicite est environ 25 fois plus rapide sur mon ordinateur lorsque .n = 5000
Ensuite, j'ai écrit une version Cython du code en boucle (plus lisible).
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
Sur mon ordinateur portable, celui-ci est environ 200 fois plus rapide que la version en boucle (et 8 fois plus rapide que la version vectorisée à 1 boucle). Je suis sûr que les autres peuvent faire mieux.
J'ai joué avec une version Julia, et elle semblait (si je la chronométrais correctement) comparable au code Cython.