'What is difference between the function numpy.dot(), @, and method .dot() for matrix-matrix multiplication?

Is there any difference? If not, what is preferred by convention? The performance seems to be almost the same.

a=np.random.rand(1000,1000)
b=np.random.rand(1000,1000)
%timeit a.dot(b)     #14.3 ms ± 374 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit np.dot(a,b)  #14.7 ms ± 315 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit a @ b        #15.1 ms ± 779 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)


Solution 1:[1]

They are almost identical with a few exceptions.

a.dot(b) and np.dot(a, b) are exactly the same. See numpy.dot and ndarray.dot.

However, looking at the documentation of numpy.dot:

If both a and b are 2-D arrays, it is matrix multiplication, but using matmul or a @ b is preferred.

a @ b corresponds to numpy.matmul(a, b). dot and matmul differ as follows:

matmul differs from dot in two important ways:

  • Multiplication by scalars is not allowed, use * instead.
  • Stacks of matrices are broadcast together as if the matrices were elements, respecting the signature (n,k),(k,m)->(n,m):
>>> a = np.ones([9, 5, 7, 4])
>>> c = np.ones([9, 5, 4, 3])
>>> np.dot(a, c).shape (9, 5, 7, 9, 5, 3)
>>> np.matmul(a, c).shape (9, 5, 7, 3)
>>> # n is 7, k is 4, m is 3

Solution 2:[2]

In my opinion, what best description and explanation is a clear example:

# How and when to use dot or matmul (@) ? # suppose all B values of dense nnet is 0 

inp=np.random.random((20,10,100,4)) # 4 inputs 100 data 10 different cases 20 different groups
nnet1=np.random.random((4,3)) # 4 inputs 3 outputs 
nnet2=np.random.random((3,5)) # 3 inputs 5 outputs 
nnet3=np.random.random((5,2)) # 5 inputs 2 outputs 

test1=inp@nnet1@nnet2@nnet3
test2=inp.dot(nnet1).dot(nnet2).dot(nnet3)
print(test1.shape)
print(test2.shape)
print(test1[5,3,7,1]) #6 th data 4th case second nnet output
print(test2[5,3,7,1]) #6 th data 4th case second nnet output



inp=np.random.random((20,10,100,4)) # 4 inputs 100 data 10 different cases 20 different groups
nnet1=np.random.random((9,4,3)) # 4 inputs 3 outputs  # 9 different networks
nnet2=np.random.random((9,3,5)) # 3 inputs 5 outputs  # 9 different networks
nnet3=np.random.random((9,5,2)) # 5 inputs 2 outputs  # 9 different networks 

test1=inp@nnet1[0]@nnet2[0]@nnet3[0]  # for network 0 
test2=inp.dot(nnet1@nnet2@nnet3)
print(test1.shape)
print(test2.shape)
print(test1[5,3,7,1]) #6 th data 4th case second nnet output
print(test2[5,3,7,0,1]) # 6 th data 4th case second nnet output for network 0 

Output:

(20, 10, 100, 2)
(20, 10, 100, 2)
2.502277900709035
2.502277900709035
(20, 10, 100, 2)
(20, 10, 100, 9, 2)
0.6919054739155295
0.6919054739155295

Where you can compare and contrast each element to conditionally connect them which makes you understand deeply ...

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 iz_
Solution 2 Gediz GÜRSU