Matrix multiplication can be performed in Python using 2D lists and nested loops. This helps us understand how matrix multiplication works without using a matrix multiplication function from an external library.
For large numerical calculations, NumPy provides faster and more convenient matrix operations. Here, however, we first calculate the result manually.
Two matrices can be multiplied only when the number of columns in the first matrix is equal to the number of rows in the second matrix.
For example:
Matrix A : 2 x 3
Matrix B : 3 x 2
Result : 2 x 2
The inner dimensions, 3 and 3, must match. The result takes the outer dimensions, giving a 2 x 2 matrix.
Each value in the result is calculated by multiplying corresponding values from one row of the first matrix and one column of the second matrix, then adding the products.
Here, the first matrix has 2 rows and 3 columns. The second matrix has 3 rows and 2 columns.
M1=[
[1, 2, 3],
[4, 5, 6]
]
M2=[
[7, 8],
[9, 10],
[11, 12]
]
rows_m1=len(M1)
cols_m1=len(M1[0])
cols_m2=len(M2[0])
result=[]
for i in range(rows_m1):
row=[]
for j in range(cols_m2):
total=0
for k in range(cols_m1):
total=total+(M1[i][k] * M2[k][j])
row.append(total)
result.append(row)
for row in result:
print(row)
Output
[58, 64]
[139, 154]
The resulting matrix has two rows and two columns.
Let us calculate the first value of the result.
The first row of M1 is:
[1, 2, 3]
The first column of M2 is:
[7, 9, 11]
Multiply the corresponding values and add the results:
(1 * 7) + (2 * 9) + (3 * 11)
= 7 + 18 + 33
= 58
Therefore, the first element of the result matrix is 58.
The first row and second column produce:
(1 * 8) + (2 * 10) + (3 * 12)
= 64
The same process is repeated for every row of the first matrix and every column of the second matrix.
We can place the same logic inside a user-defined function so different matrices can be multiplied using the same code.
def multiply_matrices(M1, M2):
rows_m1=len(M1)
cols_m1=len(M1[0])
rows_m2=len(M2)
cols_m2=len(M2[0])
if cols_m1 != rows_m2:
raise ValueError("Columns of M1 must equal rows of M2")
result=[]
for i in range(rows_m1):
row=[]
for j in range(cols_m2):
total=0
for k in range(cols_m1):
total=total+(M1[i][k] * M2[k][j])
row.append(total)
result.append(row)
return result
M1=[
[1, 2, 3],
[4, 5, 6]
]
M2=[
[7, 8],
[9, 10],
[11, 12]
]
result=multiply_matrices(M1, M2)
for row in result:
print(row)
Output
[58, 64]
[139, 154]
Matrix multiplication is not valid when the number of columns in the first matrix does not equal the number of rows in the second matrix.
For example:
M1=[
[1, 2, 3],
[4, 5, 6]
]
M2=[
[1, 2],
[3, 4]
]
Here, M1 has 3 columns while M2 has only 2 rows.
The condition used in the function checks this:
if cols_m1 != rows_m2:
raise ValueError("Columns of M1 must equal rows of M2")
This prevents the multiplication from continuing with incompatible matrix dimensions.
We can also generate matrices using random numbers and apply the same multiplication logic.
In this example, M1 is a 3 x 4 matrix and M2 is a 4 x 2 matrix. The resulting matrix will therefore have dimensions 3 x 2.
from random import randrange
rows_m1=3
cols_m1=4
cols_m2=2
M1=[]
M2=[]
# Create M1 as 3 x 4
for i in range(rows_m1):
row=[]
for j in range(cols_m1):
row.append(randrange(10))
M1.append(row)
# Create M2 as 4 x 2
for i in range(cols_m1):
row=[]
for j in range(cols_m2):
row.append(randrange(10))
M2.append(row)
print("Matrix M1")
for row in M1:
print(row)
print("Matrix M2")
for row in M2:
print(row)
result=[]
for i in range(rows_m1):
row=[]
for j in range(cols_m2):
total=0
for k in range(cols_m1):
total=total+(M1[i][k] * M2[k][j])
row.append(total)
result.append(row)
print("Result")
for row in result:
print(row)
The values will change each time the program runs because the matrices are generated using randrange(10).
The dimensions will remain:
M1 : 3 x 4
M2 : 4 x 2
Result : 3 x 2
After calculating the result manually, we can verify it using NumPy.
import numpy as np
numpy_result=np.dot(M1, M2)
print(numpy_result)
For compatible 2D arrays, NumPy's dot() performs matrix multiplication and should produce the same numerical result as the nested-loop calculation.
NumPy arrays also support the matrix multiplication operator @.
A=np.array(M1)
B=np.array(M2)
result=A @ B
print(result)
The manual nested-loop example is useful for understanding the calculation. For practical numerical work involving larger matrices, NumPy is usually more convenient.
m x n multiplied by a matrix of size n x p produces a matrix of size m x p.randrange().dot() or the @ operator can be used to verify or perform matrix multiplication.Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.