5 Best Ways to Multiply Two Matrices in Python by Passing Them to a Function

πŸ’‘ Problem Formulation: In mathematical computations and computer programming, multiplying two matrices is a fundamental operation. Specifically, for Python programming, a challenge often encountered is to multiply two matrices by passing them as arguments to a function. Consider matrix A with dimensions n x m and matrix B with dimensions m x p; the article aims to showcase different methods to produce their product, matrix C, with dimensions n x p, passing A and B to a multiplication function. For example, if A is a 2×3 matrix and B a 3×2 matrix, the desired output is a 2×2 matrix C where each element c[i][j] is the sum of the products of corresponding elements from the i-th row of A and the j-th column of B.

Method 1: Nested Loops

The nested loops method for matrix multiplication in Python is a straightforward approach involving three nested for-loops. The function takes two matrices as inputs and returns the resulting matrix. This method reflects the definition of matrix multiplication by iterating over rows of the first matrix and columns of the second matrix.

Here’s an example:

def matrix_multiply(A, B):
    result = [[sum(a * b for a, b in zip(A_row, B_col)) for B_col in zip(*B)] for A_row in A]
    return result

# 2x3 Matrix
A = [[1, 2, 3],
     [4, 5, 6]]

# 3x2 Matrix
B = [[7, 8],
     [9, 10],
     [11, 12]]

print(matrix_multiply(A, B))

Output:

[[58, 64],
 [139, 154]]

The code defines a function matrix_multiply() that multiplies two matrices by utilizing nested list comprehension, representing three nested loops. The zip() function transposes matrix B and iterates in a pair-wise manner with elements from A. The product of corresponding elements is calculated and summed to form an element of the resulting matrix. This method is simple but may be inefficient for large matrices due to explicit looping.

Method 2: Using NumPy Library

Using the NumPy library, matrix multiplication can be performed efficiently with a simple call to the numpy.dot() function or using the `@` operator. NumPy is highly optimized for numerical computations and should be the go-to method for large matrices.

Here’s an example:

import numpy as np

def numpy_matrix_multiply(A, B):
    return np.dot(A, B)

A = np.array([[1, 2, 3],
              [4, 5, 6]])

B = np.array([[7, 8],
              [9, 10],
              [11, 12]])

print(numpy_matrix_multiply(A, B))

Output:

[[ 58  64]
 [139 154]]

This snippet utilizes NumPy to multiply two matrices. The numpy_matrix_multiply() function makes use of NumPy’s dot() function, which is specifically designed for performing dot products. This method is both concise and efficient, harnessing the power of optimized C libraries under the hood of NumPy.

Method 3: Using the operator Module

Python’s built-in operator module facilitates functional programming and can be used to multiply matrices. Specifically, operator.mul() can replace the manual element-wise multiplication, making the code cleaner.

Here’s an example:

import operator

def operator_matrix_multiply(A, B):
    return [[sum(map(operator.mul, A_row, B_col)) for B_col in zip(*B)] for A_row in A]

A = [[1, 2, 3],
     [4, 5, 6]]

B = [[7, 8],
     [9, 10],
     [11, 12]]

print(operator_matrix_multiply(A, B))

Output:

[[58, 64],
 [139, 154]]

The function operator_matrix_multiply() leverages the map() and operator.mul() functions for multiplication, which handles element-wise multiplication, then the sum() aggregates the results for each row and column pair. This method is somewhat more Pythonic but does not necessarily offer a performance advantage.

Method 4: Matrix Multiplication Using itertools

The itertools module in Python provides a collection of tools for handling iterators. It can be used in the context of matrix multiplication to generate Cartesian products of the rows of the first matrix with the columns of the second, which can then be summed after element-wise multiplication.

Here’s an example:

import itertools

def itertools_matrix_multiply(A, B):
    B_transposed = list(zip(*B))
    return [[sum(itertools.starmap(operator.mul, zip(row, col))) 
             for col in B_transposed] for row in A]

A = [[1, 2, 3],
     [4, 5, 6]]

B = [[7, 8],
     [9, 10],
     [11, 12]]

print(itertools_matrix_multiply(A, B))

Output:

[[58, 64],
 [139, 154]]

In this example, itertools_matrix_multiply() uses itertools.starmap() to perform the element-wise multiplication and then calculates the sums for each result. This method may improve readability for those familiar with itertools and functional programming styles.

Bonus One-Liner Method 5: List Comprehension with zip

Python’s list comprehension can often be used to express complex operations in a single line. Here, it is combined with zip and sum for a compact matrix multiplication implementation.

Here’s an example:

A = [[1, 2, 3], [4, 5, 6]]
B = [[7, 8], [9, 10], [11, 12]]

# One-liner Matrix Multiplication
result = [[sum(a*b for a, b in zip(row, col)) for col in zip(*B)] for row in A]

print(result)

Output:

[[58, 64],
 [139, 154]]

This one-liner compresses the entire matrix multiplication process into a single list comprehension which operates identically to the nested loops approach but in a more compact form. This method is incredibly succinct but may sacrifice some readability, especially for those less comfortable with Python’s functional programming constructs.

Summary/Discussion

  • Method 1: Nested Loops. Straightforward, mirrors the manual multiplication process. Inefficient for large matrices.
  • Method 2: NumPy Library. Efficient and concise. Requires an external library and is not suited for environments where NumPy is not available.
  • Method 3: operator Module. Somewhat more Pythonic than nested loops. No significant performance benefit over the nested loops method.
  • Method 4: itertools. Improves readability with functional programming. Still not as efficient as NumPy for large matrices.
  • Method 5: One-Liner List Comprehension. Extremely compact. May be confusing to read and debug for novice Python programmers.