Función verdaderamente vectorizar para numpy array en Python.
Tengo la siguiente función, que toma dos arreglos 1D de numpy
q_i
y q_j
, realiza algunos cálculos (incluyendo calcular la norma de su diferencia) y devuelve un arreglo de numpy
:
import numpy as np
import numpy.linalg as lin
def coulomb(q_i, q_j, c1=0.8, c2=0.2, k=0.5):
"""
Parametros
----------
q_i : numpy.ndarray
q_j : numpy.ndarray
c1 : float
c2 : float
k : float
Returns
-------
numpy.ndarray
"""
q_ij = q_i - q_j
q_ij_norm = lin.norm(q_i - q_j)
f_q_i = (k * c1 * c2 / q_ij_norm ** 3) * q_ij
return f_q_i
Ahora tengo varios de estos arreglos q
almacenados en otro arreglo de numpy
t
= [q1, q2, q3, …, qn], y quiero evaluar la función coulomb
para todos los pares únicos de q_i
y q_j
dentro de t
(es decir, para (q1, q2), (q1, q3), …, (q1, qn), (q2, q3), …, (q2, qn), … (q_n-1, qn)).
¿Existe alguna forma de vectorizar este cálculo (y me refiero a realmente vectorizarlo para mejorar el rendimiento, ya que np.vectorize
solo es un bucle for
en el interior)?
Mi solución actual es un bucle for
anidado, que está lejos de ser óptimo en cuanto al rendimiento:
for i, _ in enumerate(t):
for j, _ in enumerate(t[i+1:]):
f = coulomb(t[i], t[j])
...
davy.ai
Sí, hay una forma de vectorizar este cálculo usando
np.meshgrid
ynp.triu_indices
. Aquí hay una implementación de ejemplo:Explicación:
np.triu_indices
devuelve los índices para el triángulo superior de una matriz cuadrada (excluyendo la diagonal), que es exactamente el conjunto de pares únicos que queremos calcular.np.meshgrid
crea una “rejilla” de índices a partir de los índices de fila y columna devueltos pornp.triu_indices
.q
det
, y calcular la fuerza de Coulomb para todos los pares a la vez usando la versión vectorizada decoulomb
.q_i
yq_j
es la misma que la fuerza entreq_j
yq_i
.Este enfoque es mucho más rápido que la solución con bucles anidados, ya que todos los cálculos se realizan en una sola operación vectorizada.