¿Hay una manera más rápida de agregar dos matrices numpy 2-d

Digamos que tengo dos matrices numpy 2-d grandes de las mismas dimensiones (digamos 2000×2000). Quiero sumrlos a los elementos. Me preguntaba si hay una forma más rápida que np.add ()

Edición: estoy agregando un ejemplo similar de lo que estoy usando ahora. ¿Hay una manera de acelerar esto?

#a and b are the two matrices I already have.Dimension is 2000x2000 #shift is also a list that is previously known for j in range(100000): b=np.roll(b, shift[j] , axis=0) a=np.add(a,b) 

Enfoque # 1 (vectorizado)

Podemos usar el modulus para simular el comportamiento circulante de roll/circshift y con los índices emitidos para cubrir todas las filas, tendríamos un enfoque completamente vectorizado, como así:

 n = b.shape[0] idx = n-1 - np.mod(shift.cumsum()[:,None]-1 - np.arange(n), n) a += b[idx].sum(0) 

Enfoque # 2 (Loopy uno)

 b_ext = np.row_stack((b, b[:-1] )) start_idx = n-1 - np.mod(shift.cumsum()-1,n) for j in range(start_idx.size): a += b_ext[start_idx[j]:start_idx[j]+n] 

Notación de colon vs uso de índices para rebanar

La idea aquí es hacer un trabajo mínimo una vez que estemos dentro del bucle. Estamos pre-computando el índice de la fila de inicio de cada iteración antes de entrar en el bucle. Por lo tanto, todo lo que tenemos que hacer una vez dentro del bucle es cortar con notación de dos puntos, que es una vista de la matriz y sumr. Esto debería ser mucho mejor que la rolling que necesita para computar todos esos índices de fila que resultan en una copia que es costosa.

Aquí hay un poco más en la vista y copie los conceptos cuando corte con dos puntos e índices:

 In [11]: a = np.random.randint(0,9,(10)) In [12]: a Out[12]: array([8, 0, 1, 7, 5, 0, 6, 1, 7, 0]) In [13]: a[3:8] Out[13]: array([7, 5, 0, 6, 1]) In [14]: a[[3,4,5,6,7]] Out[14]: array([7, 5, 0, 6, 1]) In [15]: np.may_share_memory(a, a[3:8]) Out[15]: True In [16]: np.may_share_memory(a, a[[3,4,5,6,7]]) Out[16]: False 

Prueba de tiempo de ejecución

Definiciones de funciones –

 def original_loopy_app(a,b): for j in range(shift.size): b=np.roll(b, shift[j] , axis=0) a += b def vectorized_app(a,b): n = b.shape[0] idx = n-1 - np.mod(shift.cumsum()[:,None]-1 - np.arange(n), n) a += b[idx].sum(0) def modified_loopy_app(a,b): n = b.shape[0] b_ext = np.row_stack((b, b[:-1] )) start_idx = n-1 - np.mod(shift.cumsum()-1,n) for j in range(start_idx.size): a += b_ext[start_idx[j]:start_idx[j]+n] 

Caso 1:

 In [5]: # Setup input arrays ...: N = 200 ...: M = 1000 ...: a = np.random.randint(11,99,(N,N)) ...: b = np.random.randint(11,99,(N,N)) ...: shift = np.random.randint(0,N,M) ...: In [6]: original_loopy_app(a1,b1) ...: vectorized_app(a2,b2) ...: modified_loopy_app(a3,b3) ...: In [7]: np.allclose(a1, a2) # Verify results Out[7]: True In [8]: np.allclose(a1, a3) # Verify results Out[8]: True In [9]: %timeit original_loopy_app(a1,b1) ...: %timeit vectorized_app(a2,b2) ...: %timeit modified_loopy_app(a3,b3) ...: 10 loops, best of 3: 107 ms per loop 10 loops, best of 3: 137 ms per loop 10 loops, best of 3: 48.2 ms per loop 

Caso # 2:

 In [13]: # Setup input arrays (datasets are exactly 1/10th of original sizes) ...: N = 200 ...: M = 10000 ...: a = np.random.randint(11,99,(N,N)) ...: b = np.random.randint(11,99,(N,N)) ...: shift = np.random.randint(0,N,M) ...: In [14]: %timeit original_loopy_app(a1,b1) ...: %timeit modified_loopy_app(a3,b3) ...: 1 loops, best of 3: 1.11 s per loop 1 loops, best of 3: 481 ms per loop 

Por lo tanto, ¡estamos viendo una aceleración de 2x+ allí con el enfoque descabellado modificado!