Related
I want to multiply two 3D tensors in a specific way.
The two tensors have shapes T1 = (a,b,c) and T2 = (d,b,c).
What I want is to multiply a times T2 by the successive 'slices' (b,c) of a.
In other words, I want to have the same as this code :
import numpy as np
a=2
b=3
c=4
d=5
T1 = np.random.rand(a,b,c)
T2 = np.random.rand(d,b,c)
L= []
for j in range(a) :
L+=[T1[j,:,:]*T2]
L = np.array(L)
L.shape
I have the iterative solution and I try with axes arguments but I didn't succeed in the second way.
Ok, now I think I got the solution:
a=2
b=3
c=4
d=5
T1 = np.random.rand(a,b,c)
T2 = np.random.rand(d,b,c)
L = np.zeros(shape=(a,d,b,c))
for i1 in range(len(T1)):
for i2 in range(len(T2)):
L[i1,i2] = np.multiply(np.array(T1[i1]),np.array(T2[i2]))
Since the shapes:
In [26]: T1.shape, T2.shape
Out[26]: ((2, 3, 4), (5, 3, 4))
produce a:
In [27]: L.shape
Out[27]: (2, 5, 3, 4)
Let's try a broadcasted pair of arrays:
In [28]: res = T1[:,None]*T2[None,:]
Shape and values match:
In [29]: res.shape
Out[29]: (2, 5, 3, 4)
In [30]: np.allclose(L,res)
Out[30]: True
tensordot, dot, or matmul don't apply; just plain elementwise multiplication, with broadcasting.
This is my code in python, the dimension of sx should be of 100X4 and sy 100X1 by the multiplication (sx)(B)(sy).
import numpy as np
B= [[-6.08066634428988e-10, -8.61023850910464e-11, 5.48222828615260e-12, -9.49229025004441e-14],
[-3.38148313553674e-11, 6.47759097087283e-12, 1.14900158474371e-13, -5.70078947874486e-15],
[-2.55893304237669e-13, -1.40941560399352e-13, 5.76510238931847e-15, -5.52980385181738e-17],
[3.39795122177475e-15, 7.95704191204353e-16, -5.31260642039813e-17, 7.83532802015832e-19]]
[X, Y] = np.meshgrid(np.arange(0, 3, 0.01*3),np.arange(0, 15, 0.01*(15)))
sx=[]
sy=[]
F=[]
for i in range(len(X)):
for j in range(len(X)):
for k in range(len(B)):
sx[i,k].append(X[i,j]**k)
for l in range(len(B)):
sy[l].append((Y[i,j]**l))
F[i,j] = sx*B*sy
The error:
sx[i,k].append(X[i,j]**k) TypeError: list indices must be integers or slices, not tuple
MATLAB code copied from comment (guess as to formatting)
[x,y]=meshgrid(0:0.01*3:3,0:0.01*15:15);
for i=1:size(x)
for j=1:size(x)
for k=0:size(B) -1
sx(1,k+1)=(x(i,j)^k);
end
for k=0:size(B) -1
sy(k+1,1)=(y(i,j)^k);
end
G(i,j)=sx*B*sy;
end
end
If sx or X is a 2D list then indices must be [i][j]. If you're trying to append to two indices i and j then it should be separate calls to append.
In an Octave session:
B =
-6.0807e-10 -8.6102e-11 5.4822e-12 -9.4923e-14
-3.3815e-11 6.4776e-12 1.1490e-13 -5.7008e-15
-2.5589e-13 -1.4094e-13 5.7651e-15 -5.5298e-17
3.3980e-15 7.9570e-16 -5.3126e-17 7.8353e-19
>>
>> [x,y]=meshgrid(0:0.01*3:3,0:0.01*15:15);
>> for i=1:size(x)
for j=1:size(x)
for k=0:size(B) -1
sx(1,k+1)=(x(i,j)^k);
end
for k=0:size(B) -1
sy(k+1,1)=(y(i,j)^k);
end
G(i,j)=sx*B*sy;
end
end
produces
x, y, G (101 x 101)
>> sx (1,4)
sx =
1 3 9 27
>> sy (4,1)
sy =
1
15
225
3375
So the G element is (1,4) * (4,4) * (4,1) => (1,1)
Looks like I should be able to make a
In [100]: B= [[-6.08066634428988e-10, -8.61023850910464e-11, 5.48222828615260e-12, -9.49229025004441e-14],
...: [-3.38148313553674e-11, 6.47759097087283e-12, 1.14900158474371e-13, -5.70078947874486e-15],
...: [-2.55893304237669e-13, -1.40941560399352e-13, 5.76510238931847e-15, -5.52980385181738e-17],
...: [3.39795122177475e-15, 7.95704191204353e-16, -5.31260642039813e-17, 7.83532802015832e-19]]
...:
In [101]: B = np.array(B)
In [106]: [X, Y] = np.meshgrid(np.linspace(0, 3, 101),np.linspace(0, 15, 101),indexing='ij')
In [107]: X.shape
Out[107]: (101, 101)
In [108]: k = np.arange(0,4)
In [109]: k
Out[109]: array([0, 1, 2, 3])
In [110]: SX = X[:,:,None]**k # (101,101,4)
In [111]: SY = Y[:,:,None]**k
In [114]: G = np.einsum('ijk,kl,ijl->ij',SX,B,SY)
In [115]: G.shape
Out[115]: (101, 101)
Allowing for the "F" order of MATLAB (ie. transpose), looks like these results match:
>> G(1,1)
ans = -0.00000000060807
In [118]: G[0,0]
Out[118]: -6.08066634428988e-10
>> G(50,23)
ans = -0.00000000097117
In [119]: G[22,49]
Out[119]: -9.71172989297259e-10
With broadcasting I don't to make the meshgrid arrays
In [121]: x, y = np.linspace(0,3,101), np.linspace(0,15,101)
In [124]: sx = x[:,None]**k
In [125]: sy = y[:,None]**k
In [126]: sx.shape
Out[126]: (101, 4)
In [129]: g = sx#B#sy.T
In [130]: g.shape
Out[130]: (101, 101)
In [131]: np.allclose(G,g)
Out[131]: True
Here I'm doing a matrix product of
(101,4) (4,4) (4,100) => (101,101)
I don't udnerstand how tensordot works and I was reading the official documentation but I don't understand at all what is happening there.
a = np.arange(60.).reshape(3,4,5)
b = np.arange(24.).reshape(4,3,2)
c = np.tensordot(a,b, axes=([1,0],[0,1]))
c.shape
(5, 2)
Why is the shape (5, 2)? What exactly is happening?
I also read this article but the answer is confusing me.
In [7]: A = np.random.randint(2, size=(2, 6, 5))
...: B = np.random.randint(2, size=(3, 2, 4))
...:
In [9]: np.tensordot(A, B, axes=((0),(1))).shape
Out[9]: (6, 5, 3, 4)
A : (2, 6, 5) -> reduction of axis=0
B : (3, 2, 4) -> reduction of axis=1
Output : `(2, 6, 5)`, `(3, 2, 4)` ===(2 gone)==> `(6,5)` + `(3,4)` => `(6,5,3,4)`
Why is the shape (6, 5, 3, 4)?
In [196]: a = np.arange(60.).reshape(3,4,5)
...: b = np.arange(24.).reshape(4,3,2)
...: c = np.tensordot(a,b, axes=([1,0],[0,1]))
In [197]: c
Out[197]:
array([[4400., 4730.],
[4532., 4874.],
[4664., 5018.],
[4796., 5162.],
[4928., 5306.]])
I find the einsum equivalent to be easier to "read":
In [198]: np.einsum('ijk,jil->kl',a,b)
Out[198]:
array([[4400., 4730.],
[4532., 4874.],
[4664., 5018.],
[4796., 5162.],
[4928., 5306.]])
tensordot works by transposing and reshaping the inputs to reduce the problem to a simple dot:
In [204]: a1 = a.transpose(2,1,0).reshape(5,12)
In [205]: b1 = b.reshape(12,2)
In [206]: np.dot(a1,b1) # or a1#b1
Out[206]:
array([[4400., 4730.],
[4532., 4874.],
[4664., 5018.],
[4796., 5162.],
[4928., 5306.]])
tensordot can do further manipulation to the result, but that's not needed here.
I had to try several things before I got a1/b1 right. For example a.transpose(2,0,1).reshape(5,12) produces the right shape, but different values.
yet another version:
In [210]: (a.transpose(1,0,2)[:,:,:,None]*b[:,:,None,:]).sum((0,1))
Out[210]:
array([[4400., 4730.],
[4532., 4874.],
[4664., 5018.],
[4796., 5162.],
[4928., 5306.]])
Lets consider I have an array of shape (1, 3, 4, 4) and I apply numpy.sum() on this and reduce against axes [2,3]. Below is a sample code --
import numpy as np
data = np.random.rand(1, 3, 4, 4)
res = np.sum(data, axis=(2,3), keepdims=True)
How many addition operations are being done by np.sum()?
In [202]: data = np.arange(3*4*4).reshape(1,3,4,4)
do your sum:
In [203]: res = np.sum(data, axis=(2,3), keepdims=True)
In [204]: res
Out[204]:
array([[[[120]],
[[376]],
[[632]]]])
In [205]: res.shape
Out[205]: (1, 3, 1, 1)
to produce each of the 3 sums:
In [207]: for i in range(3):
...: print(data[0,i].sum())
...:
120
376
632
And in a more detailed simulation (for one of those 3):
In [208]: tot=0
...: for i in range(4):
...: for j in range(4):
...: tot += data[0,0,i,j]
...:
In [209]: tot
Out[209]: 120
I'll let you count the +=.
I am a noobie to python and numpy (and programming in general). I am trying to speed up my code as much as possible. The math involves several summations over multiple axes of a few arrays. I've attained one level of vectorization, but I can't seem to get any deeper than that and have to resort to for loops (I believe there's three levels of recursion, M, N, and I, one of which I've eliminated, I). Here's my code for the relevant section (this code works, but I'd like to speed it up):
def B1(n, i):
return np.pi * n * dmaxi * (-1)**(n+1) * np.sin(qi[i]*dmaxi) * ((np.pi*n)**2 - (qi[i]*dmaxi)**2)**(-1)
for n in N:
B[n, :] = B1(n, I)
for m in M:
for n in N:
C[m, n] = np.dot((1/np.square(qi*Iq[0, :, 2]))*B[m, :], B[n, :])
Y[m] = np.dot((1/np.square(qi*Iq[0, :, 2]))*U[0, :, 1], B[m, :])
A = np.linalg.solve(C[1:, 1:], (0.25)*Y[1:])
dmaxi is just a float and m, n and i are integers. The arrays have the following shapes:
>>> qi.shape
(551,)
>>> N.shape
(18,)
>>> M.shape
(18,)
>>> I.shape
(551,)
>>> Iq.shape
(1, 551, 3)
>>> U.shape
(1, 551, 3)
As you can see I've vectorized the calculation of the 2nd axis of B, but I can't seem to do it for the 1st axis, C, and Y, which still require the for loops. It seems that when I try to do the same form of vectorization that I did for the 1st axis of B (define a function, then give the array as the argument), I get a broadcasting error since it appears to be trying to calculate both axes simultaneously, rather than the 1st, then the 2nd, which is why I had to force it into a for loop instead. The same problem occurs for both C and Y which is why they're both in for loops also. In case that's confusing, essentially what I tried was:
>>> B[:, :] = B1(N, I)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "sasrec_v6.py", line 155, in B1
return np.pi * n * dmaxi * (-1)**(n+1) * np.sin(qi[i]*dmaxi) * ((np.pi*n)**2 - (qi[i]*dmaxi)**2)**(-1)
ValueError: operands could not be broadcast together with shapes (18) (551)
Vectorizing the 2nd axis of B made a substantial improvement to the speed of my code, so I'm assuming that the same will apply for further vectorization (I hope I'm using that term correctly by the way).
You can use broadcasting to make 2d arrays from your 1d index vectors. I haven't tested these yet, but they should work:
If you reshape the N to be a column vector, then B1 will return a 2d array:
B[N] = B1(N[:, None], I)
For Y and C, I'd use np.einsum to have better control over which axes are mulitplied (probably this could be done with np.dot as well but I'm not sure how.
C[M[:, None], N] = np.einsum('ij,kj->ik',
B[M]/np.square(qi*Iq[0, :, 2]),
B[N])
Y[M] = np.einsum('i, ki->k',
U[0, :, 1]/np.square(qi*Iq[0, :, 2]),
B[M])
To see what that indexing trick does:
In [1]: a = np.arange(3)
In [2]: a
Out[2]: array([0, 1, 2])
In [3]: a[:, None]
Out[3]:
array([[0],
[1],
[2]])
In [4]: b = np.arange(4,1,-1)
In [5]: b
Out[5]: array([4, 3, 2])
In [6]: a[:, None] * b
Out[6]:
array([[0, 0, 0],
[4, 3, 2],
[8, 6, 4]])
It saves two orders of magnitude in time:
In [92]: %%timeit
....: B = np.zeros((18, 551))
....: C = np.zeros((18, 18))
....: Y = np.zeros((18))
....: for n in N:
....: B[n, :] = B1(n, I)
....: for m in M:
....: for n in N:
....: C[m, n] = np.dot((1/np.square(qi*Iq[0, :, 2]))*B[m, :], B[n, :])
....: Y[m] = np.dot((1/np.square(qi*Iq[0, :, 2]))*U[0, :, 1], B[m, :])
....:
100 loops, best of 3: 15.8 ms per loop
In [93]: %%timeit
....: Bv = np.zeros((18, 551))
....: Cv = np.zeros((18, 18))
....: Yv = np.zeros((18))
....: Bv[N] = B1(N[:, None], I)
....: Cv[M[:, None], N] = np.einsum('ij,kj->ik', B[M]/np.square(qi*Iq[0, :, 2]), B[N])
....: Yv[M] = np.einsum('i, ki->k', U[0, :, 1]/np.square(qi*Iq[0, :, 2]), B[M])
....:
1000 loops, best of 3: 1.34 ms per loop
Here's my test:
import numpy as np
# make fake data:
np.random.seed(5)
qi = np.random.rand(551)
N = np.random.randint(0,18,18)#np.arange(18)
M = np.random.randint(0,18,18)#np.arange(18)
I = np.arange(551)
Iq = np.random.rand(1, 551, 3)
U = np.random.rand(1, 551, 3)
B = np.zeros((18, 551))
C = np.zeros((18, 18))
Y = np.zeros((18))
Bv = np.zeros((18, 551))
Cv = np.zeros((18, 18))
Yv = np.zeros((18))
dmaxi = 1.
def B1(n, i):
return np.pi * n * dmaxi * (-1)**(n+1) * np.sin(qi[i]*dmaxi) * ((np.pi*n)**2 - (qi[i]*dmaxi)**2)**(-1)
for n in N:
B[n, :] = B1(n, I)
for m in M:
for n in N:
C[m, n] = np.dot((1/np.square(qi*Iq[0, :, 2]))*B[m, :], B[n, :])
Y[m] = np.dot((1/np.square(qi*Iq[0, :, 2]))*U[0, :, 1], B[m, :])
Bv[N] = B1(N[:, None], I)
print "B correct?", np.allclose(Bv, B)
# np.einsum test case:
n, m = 2, 3
a = np.arange(n*m).reshape(n,m)*8 + 2
b = np.arange(n*m)[::-1].reshape(n,m)
c = np.empty((n,n))
for i in range(n):
for j in range(n):
c[i,j] = np.dot(a[i],b[j])
cv = np.einsum('ij,kj->ik', a, b)
print "einsum test successful?", np.allclose(c,cv)
Cv[M[:, None], N] = np.einsum('ij,kj->ik',
B[M]/np.square(qi*Iq[0, :, 2]),
B[N])
print "C correct?", np.allclose(Cv, C)
Yv[M] = np.einsum('i, ki->k',
U[0, :, 1]/np.square(qi*Iq[0, :, 2]),
B[M])
print "Y correct?", np.allclose(Yv, Y)
output :D
B correct? True
einsum test successful? True
C correct? True
Y correct? True