Placement Prep

Python Matrix Multiplication: Programs and Examples

Learn matrix multiplication in Python with nested loops and NumPy. Verified programs, step-by-step examples, and CRT placement test patterns for engineering students.

By FACE Prep Team 5 min read
python matrix-multiplication numpy placement-prep crt-training coding-programs

Matrix multiplication in Python has two forms: a three-nested-loop manual version that shows exactly how the arithmetic works, and a NumPy call that runs the same logic at C speed.

Both are testable in campus placement rounds. Knowing when each applies, and being able to produce correct code for either, covers the full range of questions that appear in CRT sessions and online hiring assessments.

The Dimensional Rule: When Matrix Multiplication Works

Matrix multiplication is defined only when the number of columns in the first matrix equals the number of rows in the second.

If matrix A has shape (m × n) and matrix B has shape (n × p), the result has shape (m × p). The n disappears; it is the shared dimension over which each row-column dot product accumulates.

Quick examples:

Matrix A shapeMatrix B shapeResult shapeValid?
2 × 33 × 22 × 2Yes
2 × 32 × 3No
3 × 11 × 43 × 4Yes
4 × 44 × 44 × 4Yes

In the manual loop version, Python does not validate this for you. If the shapes are wrong, the code either throws an IndexError or silently produces a wrong-shaped result. NumPy raises a ValueError immediately with a clear message. Add a dimension check before running any production code.

Manual Implementation: Three Nested Loops

The algorithm has three layers:

  • Outer loop: iterate over each row index i of matrix A.
  • Middle loop: iterate over each column index j of matrix B.
  • Inner loop: accumulate the dot product over index k (shared dimension).

Here is a general-purpose function that works for any valid (m × n) and (n × p) pair, not just square matrices:

def matrix_multiply(A, B):
    rows_A = len(A)
    cols_A = len(A[0])
    cols_B = len(B[0])

    # Initialise result matrix with zeros
    result = [[0] * cols_B for _ in range(rows_A)]

    for i in range(rows_A):        # rows of A
        for j in range(cols_B):    # columns of B
            for k in range(cols_A):
                result[i][j] += A[i][k] * B[k][j]

    return result

The initialisation on the line [[0] * cols_B for _ in range(rows_A)] creates a fresh zero matrix. Using [[0] * cols_B] * rows_A is a common error: it creates multiple references to the same list, so writing to one row modifies all rows.

Knowing the accumulator pattern from sum-of-array problems helps: the inner loop is exactly the same running-total logic, applied to a row-column pair instead of a flat list.

Sample Programs with Verified Outputs

Example 1: 2×2 matrices — general case

  • A = [[10, 9], [8, 6]]
  • B = [[1, 2], [3, 4]]
  • Step-by-step (re-derived from first principles):
    • result[0][0] = 10×1 + 9×3 = 10 + 27 = 37
    • result[0][1] = 10×2 + 9×4 = 20 + 36 = 56
    • result[1][0] = 8×1 + 6×3 = 8 + 18 = 26
    • result[1][1] = 8×2 + 6×4 = 16 + 24 = 40
X = [[10, 9], [8, 6]]
Y = [[1, 2], [3, 4]]
result = [[0, 0], [0, 0]]

for i in range(len(X)):
    for j in range(len(Y[0])):
        for k in range(len(Y)):
            result[i][j] += X[i][k] * Y[k][j]

for row in result:
    print(row)

Output:

[37, 56]
[26, 40]

Example 2: Square matrix self-multiplication

  • A = [[1, 2], [3, 4]]
  • B = [[1, 2], [3, 4]]
  • Step-by-step:
    • result[0][0] = 1×1 + 2×3 = 1 + 6 = 7
    • result[0][1] = 1×2 + 2×4 = 2 + 8 = 10
    • result[1][0] = 3×1 + 4×3 = 3 + 12 = 15
    • result[1][1] = 3×2 + 4×4 = 6 + 16 = 22
A = [[1, 2], [3, 4]]
B = [[1, 2], [3, 4]]
result = [[0, 0], [0, 0]]

for i in range(len(A)):
    for j in range(len(B[0])):
        for k in range(len(B)):
            result[i][j] += A[i][k] * B[k][j]

for row in result:
    print(row)

Output:

[7, 10]
[15, 22]

Note: A × A is not the same as A raised to the power 2 element-wise. The ** operator on a list is not valid anyway. Matrix exponentiation is its own operation.

For more Python basic programs that use the same loop structures, the linked article covers a range of similar exercises at CRT difficulty.

NumPy and the @ Operator

NumPy provides two routes to matrix multiplication for 2D arrays:

  • np.dot(A, B) — the established NumPy function, documented at numpy.org.
  • A @ B — the infix operator added in Python 3.5 via PEP 465. Reads like standard mathematical notation; equivalent to numpy.matmul(A, B) for 2D inputs.

For 2D arrays the three give identical results. The difference appears with higher-dimensional inputs: @ and matmul() treat a 3D array as a stack of 2D matrices; np.dot() uses different broadcasting rules. For standard CRT-level problems with two 2D matrices, any of the three works.

import numpy as np

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

result_dot = np.dot(A, B)
result_at  = A @ B

print(result_dot)
print(result_at)

Output (both produce the same matrix):

[[ 7 10]
 [15 22]]
[[ 7 10]
 [15 22]]

NumPy’s implementations call into optimised BLAS and LAPACK routines. For small matrices like the 2×2 examples above, the runtime difference versus the loop version is negligible. For matrices with hundreds of rows and columns, the loop version becomes slow enough to fail timed online assessments. The NumPy version scales.

Where This Appears in Placement Coding Tests

Matrix multiplication shows up in two forms in CRT and online assessment rounds:

  • Manual loop variant: the question provides two 2D lists and asks you to print the product matrix. This tests whether you can initialise the result correctly, set up three loops, and accumulate without using built-ins. CRT rounds at Tier-2 and Tier-3 colleges use this format.
  • NumPy variant: the question asks you to apply np.dot() or A @ B and may follow up with questions about shapes, reshaping, or the difference between element-wise and matrix multiplication. This appears in data science and analytics hiring tracks.

For AMCAT-style coding sections, the manual loop version is more common. The question usually specifies input as two 2D lists and expects the product printed row by row.

Two things trip students up consistently. First, the initialisation error described earlier (list reference vs. independent rows). Second, the loop order: the inner loop iterates over k (shared dimension), not over j. Getting those two right in under ten minutes is the actual test.

The calculator program in Python covers arithmetic operators and the accumulator loop, the two pieces that combine in the matrix multiplication inner loop. Worth revisiting if the dot-product formula above feels shaky.

The three-loop accumulator that produces [[37, 56], [26, 40]] is the manual version of what np.dot() runs in C. If you want to time both approaches and push the input sizes toward the matrix shapes used in actual model layers, TinkerLLM’s Python environment (₹299) runs without any local setup and gives you a live interpreter to experiment.

Primary sources

Frequently asked questions

What error does Python raise when matrix dimensions do not match?

The manual loop version does not validate dimensions automatically, so mismatched shapes produce garbage values without raising an error. NumPy raises a ValueError with a message like 'shapes (2,3) and (2,3) not aligned' when the inner dimensions do not match.

What is the difference between * and @ for NumPy arrays?

* performs element-wise multiplication and requires both arrays to have the same shape. @ computes the matrix product and was introduced via PEP 465 in Python 3.5. For 2D arrays, A @ B is equivalent to np.dot(A, B).

Can Python multiply non-square matrices?

Yes. A matrix with shape (m, n) can multiply a matrix with shape (n, p) to produce a result with shape (m, p). The only rule is that the column count of the first matrix must equal the row count of the second.

How many nested loops does manual matrix multiplication need in Python?

Three. One iterates over rows of A, one over columns of B, and the innermost loop accumulates the dot product over the shared dimension k. Time complexity is O(m * n * p) for an (m, n) by (n, p) multiplication.

Do placement tests ask for NumPy or the loop version?

CRT rounds at Tier-2 and Tier-3 colleges typically ask for the manual loop version because it tests nested-loop reasoning and accumulator logic. NumPy questions appear in data science and analytics roles where built-in efficiency is the point.

What is numpy.matmul() and when should I use it instead of np.dot()?

For 2D matrices, numpy.matmul() and np.dot() give identical results. The difference appears with higher-dimensional inputs: matmul() does not allow scalar multiplication and handles stacked matrices differently. For standard 2D placement problems, either works.

Build AI projects

A self-paced playground for building with LLMs.

TinkerLLM is FACE Prep's sister property. A guided environment for shipping real LLM applications, the kind of project that earns a paragraph on your resume, not a line.

Try TinkerLLM (₹299 launch)
Free AI Roadmap PDF