Loopless 3D Array Multiplication - python

Very similar to https://math.stackexchange.com/q/3615927/419686, but different.
I have 2 matrices (A with shape (5,2,3) and B with shape (6,3,8)), and I want to perform some kind of multiplication in order to take a new matrix with shape (5,6,2,8).
Python code:
import numpy as np
np.random.seed(1)
A = np.random.randint(0, 10, size=(5,2,3))
B = np.random.randint(0, 10, size=(6,3,8))
C = np.zeros((5,6,2,8))
for i in range(A.shape[0]):
for j in range(B.shape[0]):
C[i,j] = A[i].dot(B[j])
Is it possible to do the above operation without using a loop?

In [52]: np.random.seed(1)
...: A = np.random.randint(0, 10, size=(5,2,3))
...: B = np.random.randint(0, 10, size=(6,3,8))
...:
...: C = np.zeros((5,6,2,8))
...: for i in range(A.shape[0]):
...: for j in range(B.shape[0]):
...: C[i,j] = A[i].dot(B[j])
...:
np.dot does broadcast the outer dimensions:
In [53]: D=np.dot(A,B)
In [54]: C.shape
Out[54]: (5, 6, 2, 8)
In [55]: D.shape
Out[55]: (5, 2, 6, 8)
The axes order is different, but we can easily change that:
In [56]: np.allclose(C, D.transpose(0,2,1,3))
Out[56]: True
In [57]: np.allclose(C, np.swapaxes(D,1,2))
Out[57]: True
From the np.dot docs:
dot(a, b)[i,j,k,m] = sum(a[i,j,:] * b[k,:,m])

Use np.einsum which is very powerful:
C = np.einsum('aij, bjk -> abik', A, B)

Related

multiplying "across" in two numpy arrays

Given two numpy arrays of shape (25, 2), and (2,), one can easily multiply them across:
import numpy as np
a = np.random.rand(2, 25)
b = np.random.rand(2)
(a.T * b).T # ok, shape (2, 25)
I have a similar situation where b is of shape (2, 4), and I'd like to get the same results as above for all "4" b. The following works,
a = np.random.rand(25, 2)
b = np.random.rand(2, 4)
c = np.moveaxis([a * bb for bb in b.T], -1, 0) # shape (2, 4, 25)
but I have a hunch that this is possible without moveaxis.
Any ideas?
In [185]: a = np.random.rand(2, 25)
...: b = np.random.rand(2)
The multiplication is possible with broadcasting:
In [186]: a.shape
Out[186]: (2, 25)
In [187]: a.T.shape
Out[187]: (25, 2)
In [189]: (a.T*b).shape
Out[189]: (25, 2)
(25,2) * (2,) => (25,2) * (1,2) => (25,2). The transpose is a moveaxis, changing the result to (2,25)
In your second case.
In [191]: c = np.moveaxis([a * bb for bb in b.T], -1, 0)
In [192]: c.shape
Out[192]: (2, 4, 25)
In [193]: np.array([a * bb for bb in b.T]).shape
Out[193]: (4, 25, 2)
b.T is (4,2), so bb is (2,); with the (25,2) a, produces (25,2) as above. add in the (4,) iteration.
(25,1,2) * (1,4,2) => (25,4,2), which can be transposed to (2,4,25)
In [195]: (a[:,None]*b.T).shape
Out[195]: (25, 4, 2)
In [196]: np.allclose((a[:,None]*b.T).T,c)
Out[196]: True
(2,4,1) * (2,1,25) => (2,4,25)
In [197]: (b[:,:,None] * a.T[:,None]).shape
Out[197]: (2, 4, 25)
In [198]: np.allclose((b[:,:,None] * a.T[:,None]),c)
Out[198]: True
An alternative with numpy.einsum:
np.einsum('ij,jk->jki', a, b)
Check results are the same:
(np.einsum('ij,jk->jki', a, b) == c).all()
True

numpy.dot as part of a vectorized operation

Say I have three numpy arrays and I want to perform a calculation over them:
a = np.array([[1,2,3,4,5,6,7],[1,2,3,4,5,6,7],[1,2,3,4,5,6,7],[1,2,3,4,5,6,7],
[1,2,3,4,5,6,7]]) #shape is (5,7)
b = np.array([[11],[12],[11],[12],[11]]) #shape is (5,1)
c = np.array([[10],[20],[30],[40],[50],[60],[70]]) #shape is (5,1)
The calculation is: 10 + (b(rows) * (c . a(rows)))
Where c . a is the dot product of C and the row of a.
By rows, I mean doing it as a vector where I need my result to be (7,1) (one row per each column I have on a)
I'm trying to do something like:
result = 10 + (b[:][:] * (np.dot(c.T, a[:]) + b))
But this fails the np.dot operation with shapes being misaligned for that numpy.dot operation. I'm trying to figure out how to perform the calculation above as a one-liner (no for loops) in a way that Python will interpret the vectorized operation, especially for that np.dot part.
Any hints?
Thanks for your time
EDIT: this is a for loop that solves my problem. I'd like to replace that for loop with one Python line.
iBatchSize = a.shape[0]
iFeatureCount = a.shape[1]
result = np.zeros((iBatchSize,1))
for i in range(iBatchSize):
for j in range(iFeatureCount):
result [i] = 10 + (b[i][0] * (np.dot(c.T, a[i]) + b))
EDIT 2: Corrected array a with the correct array
EDIT 3: Corrected expected shape for result
In [31]: a = np.array([[1,2],[2,3],[3,4],[4,5],[5,6],[6,7],[7,8]]) #shape is (5,7)
...: b = np.array([[11],[12],[11],[12],[11]]) #shape is (5,1)
...: c = np.array([[10],[20],[30],[40],[50],[60],[70]]) #shape is (7,1)
In [32]: a.shape, b.shape, c.shape
Out[32]: ((7, 2), (5, 1), (7, 1))
a.shape does not match the comment.
In [33]: iBatchSize = a.shape[0]
...: iFeatureCount = a.shape[1]
...:
...: result = np.zeros((iBatchSize,1))
...:
...: for i in range(iBatchSize):
...: for j in range(iFeatureCount):
...: result [i] = 10 + (b[i][0] * (np.dot(c.T, a[i]) + b))
...:
Traceback (most recent call last):
File "<ipython-input-33-717691add3dd>", line 8, in <module>
result [i] = 10 + (b[i][0] * (np.dot(c.T, a[i]) + b))
File "<__array_function__ internals>", line 6, in dot
ValueError: shapes (1,7) and (2,) not aligned: 7 (dim 1) != 2 (dim 0)
np.dot is raising that error. It expects the last of first arg to match with the 2nd to the last (or only) of second arg:
In [34]: i
Out[34]: 0
In [35]: c.T.shape
Out[35]: (1, 7)
In [37]: a[i].shape
Out[37]: (2,)
This dot works:
In [38]: np.dot(c.T,a).shape # (1,7) with (7,2) => (1,2)
Out[38]: (1, 2)
====
With the correct a,
10 + (b[i][0] * (np.dot(c.T, a[i]) + b))
is (5,1) array (because of the +b), which can't be put in result[i].
===
a simple dot of a and c produces a (5,1) which can be combined with b (either with + or * or both), resulting in a (5,1) array:
In [68]: np.dot(a,c).shape
Out[68]: (5, 1)
In [69]: b*(np.dot(a,c)+b)
Out[69]:
array([[15521],
[16944],
[15521],
[16944],
[15521]])

Python code to create an array of arrays (8x8 with each being a 3x3)

I am attempting to create an array of arrays with the structure being an 8x8, where each cell in that is a 3x3 array. What I have created works, but when I want to change a specific value, I need to access it differently than I would expect.
import numpy as np
a = np.zeros((3,3))
b = np.array([[0,1,0],[1,1,1],[0,1,0]])
d = np.array([[b,a,b,a,b,a,b,a]])
e = np.array([[a,b,a,b,a,b,a,b]])
g = np.array([[d],[e],[d],[e],[d],[e],[d],[e]])
#Needed to change a specific cell
#g[0][0][0][0][0][0] = x : [Row-x][0][0][Cell-x][row-x][cell-x]
#Not sure why I have to have the 2 0's between the Row-x and the Cell-x identifiers
After this, I will need to map each value to a 24x24 grid with 1's having a different color than 0's. If anyone could provide direction to achieve this, it would be appreciated. Not looking for the specific code, but a base to understand how it can be done.
Thanks
In [291]: a = np.zeros((3,3))
...: b = np.array([[0,1,0],[1,1,1],[0,1,0]])
...: d = np.array([[b,a,b,a,b,a,b,a]])
...: e = np.array([[a,b,a,b,a,b,a,b]])
...: g = np.array([[d],[e],[d],[e],[d],[e],[d],[e]])
In [292]: a.shape
Out[292]: (3, 3)
In [293]: b.shape
Out[293]: (3, 3)
d is 4d - count the brackets: [[....]]:
In [294]: d.shape
Out[294]: (1, 8, 3, 3)
In [295]: e.shape
Out[295]: (1, 8, 3, 3)
g is (8,1) of 4 dim elements, for a total of 6. Again count the brackets:
In [296]: g.shape
Out[296]: (8, 1, 1, 8, 3, 3)
Accessing a 2d subarray, in this case equal to b:
In [298]: g[0,0,0,0,:,:]
Out[298]:
array([[0., 1., 0.],
[1., 1., 1.],
[0., 1., 0.]])
Redo, without the excess brackets:
In [299]: a = np.zeros((3,3))
...: b = np.array([[0,1,0],[1,1,1],[0,1,0]])
...: d = np.array([b,a,b,a,b,a,b,a])
...: e = np.array([a,b,a,b,a,b,a,b])
...: g = np.array([d,e,d,e,d,e,d,e])
In [300]: d.shape
Out[300]: (8, 3, 3)
In [301]: g.shape
Out[301]: (8, 8, 3, 3)

Tri-dimensional array as multiplication of vector and matrix

I have an array A (shape = (a, 1)) and matrix B (shape = (b1, b2)). Want to multiply the latter by each element of the former to generate a tridimensional array (shape = (a, b1, b2)).
Is there a vectorized way to do this?
import numpy as np
A = np.random.rand(3, 1)
B = np.random.rand(5, 4)
C = np.array([ a * B for a in A ])
There are several ways you can achieve this.
One is using np.dot, note that it will be necessary to introduce a second axis in B so both ndarrays can be multiplied:
C = np.dot(A,B[:,None])
print(C.shape)
# (3, 5, 4)
Using np.multiply.outer, as #divakar suggests:
C = np.multiply.outer(A,B)
print(C.shape)
# (3, 5, 4)
Or you could also use np.einsum:
C = np.einsum('ij,kl->ikl', A, B)
print(C.shape)
# (3, 5, 4)

Python comparing two 3 dimensional numpy arrays

I have two numpy arrays:
A.shape = (nA,x,y)
and
B.shape = (nB,x,y).
I want to find all subarrays such that
A(i,:,:) == B(j,:,:).
I know I can write a double for loop and use
np.array_equal(A(i,:,:),B(j,:,:)
However, is there a more efficient method?
You should only need to loop through one of the arrays, since you wouldn't find any additional unique subarrays after that, and you can do this with a simple list comprehension.
subarrays = [x for x in A if x in B]
If you only want the indices instead of storing the whole subarray, you can do:
indices = [x[0] for x in enumerate(A) if x[1] in B]
Utilizing Steven Rouk's solution, here is a method to get the indices for the subarrays that are equal:
indicesForMatches = [(i,j) for i,subArrayOfA in enumerate(A) for j,subArrayOfB in enumerate(B) if np.array_equal(subArrayOfA,subArrayOfB)]
You can use NumPy broadcasting for a vectorized solution, like so -
mask = ((A[:,None,:,:] == B).all(2)).all(2)
A_idx,B_idx = np.where(mask)
You can use reshaping to avoid double .all() usages and get the mask, like so -
mask = (A.reshape(A.shape[0],1,-1) == B.reshape(B.shape[0],-1)).all(-1)
Sample run -
In [41]: # Setup input arrays and force some indices to be same between A and B
...: nA = 4
...: nB = 5
...: x = 3
...: y = 2
...:
...: A = np.random.randint(0,9,(nA,x,y))
...: B = np.random.randint(0,9,(nB,x,y))
...:
...: A[2,:,:] = B[1,:,:]
...: A[3,:,:] = B[4,:,:]
...:
In [42]: mask = ((A[:,None,:,:] == B).all(2)).all(2)
...: A_idx,B_idx = np.where(mask)
...:
In [43]: A_idx, B_idx
Out[43]: (array([2, 3]), array([1, 4]))
In [44]: mask = (A.reshape(A.shape[0],1,-1) == B.reshape(B.shape[0],-1)).all(-1)
...: A_idx,B_idx = np.where(mask)
...:
In [45]: A_idx, B_idx
Out[45]: (array([2, 3]), array([1, 4]))

Categories