Here are some fully vectorized approaches -
def vectorized_app1(X):
return 1/np.exp(X[:,None] - X[...,None]).sum(1)
def vectorized_app2(X):
exp_vals = np.exp(X)
return 1/(exp_vals[:,None]/exp_vals[...,None]).sum(1)
def vectorized_app3(X):
exp_vals = np.exp(X)
return 1/np.einsum('ij,ik->ij',exp_vals,1/exp_vals)
Used Tricks and Lessons Learned
Expand sizes with None/np.newaxisto enable broadcasting and do everything in vector form.
np.exp - . , . , : exp(A-B) = exp(A)/exp(B). , np.exp(X) , .
- np.einsum. , , .
-
In [111]:
...: np.random.seed(111)
...: J,K = 110,120
...: X = np.random.rand(J,K)
...:
In [112]: %timeit original_approach(X)
1 loop, best of 3: 322 ms per loop
In [113]: %timeit vectorized_app1(X)
10 loops, best of 3: 124 ms per loop
In [114]: %timeit vectorized_app2(X)
100 loops, best of 3: 14.6 ms per loop
In [115]: %timeit vectorized_app3(X)
100 loops, best of 3: 3.01 ms per loop
, einsum 100x+ speedup!