operation of Einstein sum of 3D matrices - numpy

The following code indicates that the Einstein sum of two 3D (2x2x2) matrices is a 4D (2x2x2x2) matrix.
$ c_{ijlm} = \Sigma_k a_{i,j,k}b_{k,l,m} $
$ c_{0,0,0,0} = \Sigma_k a_{0,0,k}b_{k,0,0} = 1x9 + 5x11 = 64 $
But, c_{0,0,0,0} = 35 according to the result below:
>>> a=np.array([[[1,2],[3,4]],[[5,6],[7,8]]])
>>> b=np.array([[[9,10],[11,12]],[[13,14],[15,16]]])
>>> c=np.einsum('ijk,klm->ijlm', a,b)
>>> c
array([[[[ 35, 38],
[ 41, 44]],
[[ 79, 86],
[ 93, 100]]],
[[[123, 134],
[145, 156]],
[[167, 182],
[197, 212]]]])
Could someone explain how the operation is carried out?

The particular element that you are testing, [0,0,0,0] is calculated with:
In [167]: a[0,0,:]*b[:,0,0]
Out[167]: array([ 9, 26])
In [168]: a[0,0,:]
Out[168]: array([1, 2])
In [169]: b[:,0,0]
Out[169]: array([ 9, 13])
It may be easier to understand if we reshape both arrays to 2d:
In [170]: A=a.reshape(-1,2); B=b.reshape(2,-1)
In [171]: A
Out[171]:
array([[1, 2],
[3, 4],
[5, 6],
[7, 8]])
In [172]: B
Out[172]:
array([[ 9, 10, 11, 12],
[13, 14, 15, 16]])
In [173]: A#B
Out[173]:
array([[ 35, 38, 41, 44],
[ 79, 86, 93, 100],
[123, 134, 145, 156],
[167, 182, 197, 212]])
The same numbers, but in (4,4) instead of (2,2,2,2). It's easier to read the (1,2) and (9,13) off of A and B.

Related

Fastest way to do vectorized reduce product with boolean mask

I have a 3D numpy array A and 2D numpy boolean mask B.
The first two dimensions of A matches B
And I'm wondering if there is any fast way for each first dimension of A, select the third dimension along second based on B, perform a reduced product over the second dimension.
My expected out C would be a 2D numpy array, with the first dimension of A and the second dimension from the third of A.
My current solution is C = np.prod(A*np.repeat(B[...,np.newaxis], A.shape[-1], 2), 1)
Is there any better alternative?
With concrete example:
In [364]: A=np.arange(1,25).reshape(2,3,4); B=np.arange(1,7).reshape(2,3)
In [365]: C = np.prod(A*np.repeat(B[...,np.newaxis], A.shape[-1], 2), 1)
That repeat does:
In [366]: np.repeat(B[...,np.newaxis], A.shape[-1], 2)
Out[366]:
array([[[1, 1, 1, 1],
[2, 2, 2, 2],
[3, 3, 3, 3]],
[[4, 4, 4, 4],
[5, 5, 5, 5],
[6, 6, 6, 6]]])
In [367]: _.shape
Out[367]: (2, 3, 4)
In [368]: A*np.repeat(B[...,np.newaxis], A.shape[-1], 2)
Out[368]:
array([[[ 1, 2, 3, 4],
[ 10, 12, 14, 16],
[ 27, 30, 33, 36]],
[[ 52, 56, 60, 64],
[ 85, 90, 95, 100],
[126, 132, 138, 144]]])
But by broadcasting rules, the repeat is no needed:
In [369]: A*B[...,np.newaxis]
Out[369]:
array([[[ 1, 2, 3, 4],
[ 10, 12, 14, 16],
[ 27, 30, 33, 36]],
[[ 52, 56, 60, 64],
[ 85, 90, 95, 100],
[126, 132, 138, 144]]])
In [371]: np.prod(_369, axis=1)
Out[371]:
array([[ 270, 720, 1386, 2304],
[556920, 665280, 786600, 921600]])
You could apply prod to A and B individually, but I don't know if that makes much of a difference:
In [373]: np.prod(A,1)*np.prod(B,1)[:,None]
Out[373]:
array([[ 270, 720, 1386, 2304],
[556920, 665280, 786600, 921600]])

Combination of slicing and array index in numpy

Looking at the answers to this question: How to understand numpy's combined slicing and indexing example
I'm still unable to understand the result of indexing with a combination of a slice and two 1d arrays, like this:
>>> m = np.arange(36).reshape(3,3,4)
>>> m
array([[[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]],
[[12, 13, 14, 15],
[16, 17, 18, 19],
[20, 21, 22, 23]],
[[24, 25, 26, 27],
[28, 29, 30, 31],
[32, 33, 34, 35]]])
>>> m[1:3, [2,1],[2,1]]
array([[22, 17],
[34, 29]])
Why is the result equivalent to this?
np.array([
[m[1,2,2],m[1,1,1]],
[m[2,2,2],m[2,1,1]]
])

pytorch tensor stride - how it works

PyTorch doesn't seem to have documentation for tensor.stride().
Can someone confirm my understanding?
My questions are three-fold.
Stride is for accessing an element in the storage. So stride size will be the same as the dimension of the tensor. Correct?
For each dimension, the corresponding element of stride tells how much it takes to move along the 1-dimensional storage. Correct?
For example:
In [15]: x = torch.arange(1,25)
In [16]: x
Out[16]:
tensor([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18
, 19, 20, 21, 22, 23, 24])
In [17]: a = x.view(4,3,2)
In [18]: a
Out[18]:
tensor([[[ 1, 2],
[ 3, 4],
[ 5, 6]],
[[ 7, 8],
[ 9, 10],
[11, 12]],
[[13, 14],
[15, 16],
[17, 18]],
[[19, 20],
[21, 22],
[23, 24]]])
In [20]: a.stride()
Out[20]: (6, 2, 1)
How does having this information help perform tensor operations efficiently? Basically this is showing the memory layout. So how does it help?

Adding a row-dependent value to each row

I have a 2D array containing the following numbers:
A = [[1, 5, 9, 42],
[20, 2, 71, 0],
[2, 44, 4, 9]]
I want to add a different constant value to each row without using loops. This value is a n*c with n being the current row and c being the constant. For example, c=100 so that:
B = [[1, 5, 9, 42],
[120, 102, 171, 100],
[202, 244, 204, 209]]
Any help would be greatly appreciated
You can do that as follows:
>>> A = [[1, 5, 9, 42],
... [20, 2, 71, 0],
... [2, 44, 4, 9]]
...
>>> a = np.array(A)
>>> c = 100
>>> addto = np.arange(len(a))[:, None] * c
>>> a + addto
array([[ 1, 5, 9, 42],
[120, 102, 171, 100],
[202, 244, 204, 209]])
np.arange(len(a)) gets you a 1-dimensional array of the indices, array([0, 1, 2]), which you can then multiply by c.
The hitch is that you then need to conform this to NumPy's broadcasting rules by expanding it's dimensionality:
>>> np.arange(len(a)).shape
(3,)
>>> np.arange(len(a))[:, None].shape
(3, 1)
You could also do something like np.linspace(0, 100*(len(a)-1), num=len(a))[:, None], but that is probably overkill here.

numpy: Efficient way to use a 1D array as an index into 2D array

X.shape == (10,4)
y.shape == (10)
I'd like to produce M, where each entry in M is defined as M[r,c] == X[r, y[r]]; that is, use y to index into the appropriate column of X.
How can I do this efficiently (without loops)?
M could have a single column, though eventually I need to broadcast it so that it has the same shape as X. c starts from the first col of X (0) and goes to the last (9).
Just do :
X=np.arange(40).reshape(10,4)
Y=np.random.randint(0,4,10)
M=X[range(10),Y]
for
In [8]: X
Out[8]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15],
[16, 17, 18, 19],
[20, 21, 22, 23],
[24, 25, 26, 27],
[28, 29, 30, 31],
[32, 33, 34, 35],
[36, 37, 38, 39]])
In [9]: Y
Out[9]: array([1, 1, 3, 3, 1, 2, 2, 3, 2, 1])
In [10]: M
Out[10]: array([ 1, 5, 11, 15, 17, 22, 26, 31, 34, 37])