In the previous post we saw how matrices/vectors interact with scalar arithmetic. E.G. adding a fixed value (scalar) to a matric/vector. There is also a set of arithmetic functions involving vector to vector or matric to matrix operatons. The one I vaguely remember from a linear algrebra class is the dot product (a form of matrix multiplication). There are also other operations that may or may not be useful as we continue, but I will mention them just in case.
The one thing to keep in mind for all these operations is that the shapes of the matrices must match in some fashion or other.
Add/Subtract Two Matrices
Let’s start with something simple. We’ll look at vectors then matrices. A pattern that will continue throughout this post.
import numpy as np
# create some vectors and matrices
v1 = np.arange(1, 4)
v2 = np.array([2, 4, 6])
m1 = np.arange(1, 7).reshape(3,2)
m2 = m1 + np.arange(1, 3)
print('v1:', v1, '\nv2:', v2, '\n\nm1:\n', m1, '\n\nm2:\n', m2)
# let's try adding and subtracting the vectors
print(f"v1 + v2: {v1 + v2}")
print(f"v1 - v2: {v1 - v2}")
print(f"v2 - v1: {v2 - v1}")
# and now the matrices
print(f"m1 + m2:\n{m1 + m2}\n")
print(f"m1 - m2:\n{m1 - m2}\n")
print(f"m2 - m1:\n{m2 - m1}\n")
# now what about adding a vector to a matrix
print(f"m2 + v1:\n{m2 + v1}")
Broadcast
?? We’ll get to that later. Though you’ve already seen it in operation.
Multiplication
Now matrix multiplication has a couple of twists and turns. We’ve already looked at mutliplication by a scalar, so we will ignore that option.
Hadamard Product
If we use the Python *
multiplication operator you will get pretty much what you expect. This is known as the Hadamard product.
print(f"v1 * v2 = {v1} * {v2} = {v1 * v2}")
print(f"\nm1 * m2 =")
m1m2 = m1 * m2
print(f"[{m1[0]} [{m2[0]} [{m1m2[0]}")
print(f" {m1[1]} * {m2[1]} = {m1m2[1]}")
print(f" {m1[2]}] {m2[2]}] {m1m2[2]}]")
print(f"\nm1 * v2 = ", end='')
print(m1 * v2)
Dot Product
However that is probably not he most common product used in linear algebra. That would likely be the dot product
. I am going to ignore, at least for now, the cross product, etc.
Wasn’t going to discuss the dot product very much, but decided to have, at least, a little look at it. In the case of two vectors — of equal size — their dot product will be a scalar. For two matrices — of suitable shapes — you will get another matrix. The value in each element of the result will be the dot product of two vectors drawn from the original two matrices.
For two vectors with n
elements, the dot product is:
# let's start with our vectors
v1v2 = np.dot(v1, v2)
print(f"v1 dot v2 = v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2] = ", end='')
print(f"{v1[0]*v2[0]} + {v1[1]*v2[1]} + {v1[2]*v2[2]} = {v1v2}")
Given two matrices, a
with n
columns and m
rows and b
with p
columns and n
rows, the result for each element in the dot product is given by:
where: i = 1, …, m and
j = 1, …, p
And, the output array will have m
rows and p
columns.
The dot product of the 2 matrices is usually written as ab
or ba
. Basically, we treat the rows of a
and the columns of b
as vectors and calculate their dot product to get the entries for the result matrix. Which means that the number of columns in a
must equal the number of rows in b
.
And, the resulting matrix can have an entirely different shape from either of the two initial matrices. For example, given a 3 x 2
matrix A
and a 2 x 4
matrix B
, AB
will produce a 3 x 4
matrix result. And, BA
is not defined as in that orientation the number of columns in B
doesn’t match the number of rows in A
.
# now let's try our matrices
m1m2 = np.dot(m1, m2)