We can use the same algorithm for matrix multiplication using list of lists and Numpy arrays
In [30]: l1 = [[1,2,3],[4,5,6]] ; l2 = [[7,8],[9,10],[11,12]]
In [31]: l3 = [[sum(e1*e2 for e1, e2 in zip(row,col)) for col in zip(*l2)] for row in l1]
In [32]: a1 = np.array(l1) ; a2 = np.array(l2)
In [33]: a3 = [[sum(e1*e2 for e1, e2 in zip(row,col)) for col in zip(*a2)] for row in a1]
In [34]: a3
Out[34]: [[58, 64], [139, 154]]
In [35]: l3
Out[35]: [[58, 64], [139, 154]]
and, for a check, let's use @
, the builtin matrix multiplication operator
In [36]: a1@a2
Out[36]:
array([[ 58, 64],
[139, 154]])
Now, let's see if we can use the same matrix multiplication algorithm if our data is wrapped in a matrix class
In [37]: m1 = np.matrix(l1) ; m2 = np.matrix(l2)
In [38]: m3 = [[sum(e1*e2 for e1, e2 in zip(row,col)) for col in zip(*m2)] for row in m1]
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-38-2cd8454aa248> in <module>
----> 1 m3 = [[sum(e1*e2 for e1, e2 in zip(row,col)) for col in zip(*m2)] for row in m1]
<ipython-input-38-2cd8454aa248> in <listcomp>(.0)
----> 1 m3 = [[sum(e1*e2 for e1, e2 in zip(row,col)) for col in zip(*m2)] for row in m1]
<ipython-input-38-2cd8454aa248> in <listcomp>(.0)
----> 1 m3 = [[sum(e1*e2 for e1, e2 in zip(row,col)) for col in zip(*m2)] for row in m1]
<ipython-input-38-2cd8454aa248> in <genexpr>(.0)
----> 1 m3 = [[sum(e1*e2 for e1, e2 in zip(row,col)) for col in zip(*m2)] for row in m1]
~/lib/miniconda3/lib/python3.7/site-packages/numpy/matrixlib/defmatrix.py in __mul__(self, other)
218 if isinstance(other, (N.ndarray, list, tuple)) :
219 # This promotes 1-D vectors to row vectors
--> 220 return N.dot(self, asmatrix(other))
221 if isscalar(other) or not hasattr(other, '__rmul__') :
222 return N.dot(self, other)
ValueError: shapes (1,3) and (1,2) not aligned: 3 (dim 1) != 1 (dim 0)
Why we have this error? the dimensions are the same as before, why we have different shapes?
Here it is the reason, when we look at an element of m1
we have not a 1D array or a non-nested list, we have still another matrix
In [39]: m1[0]
Out[39]: matrix([[1, 2, 3]])
Moral of this tale?
The matrix class has an inherent stickyness that leads to this particular unexpected behaviour and to other, different, unexpected behaviours.
If one doesn't know exactly all the issues associated with using np.matrix()
, the best choice is to use 2D arrays instantiated using np.array
.
On the other hand, if one knows exactly all the issues associated with using np.matrix()
, usually they do not want to use np.matrix
.